Using superscript ⁻¹ as a postfix operator for inv?

Nice. I see they’re using the latest unicode.
Does anyone know if it will be possible (an supported by Julia) to express negative one “-1” as a superscript w/ unicode?
X^{-1} is a lot leaner than inv(X) or X^-1

1 Like

Slightly off topic, but the real answer is to never use inv. It is almost never the right answer.

8 Likes

Off-topic, but you can already do that with X\^-\^1 but the ⁻¹ is part of the variable name so you can write X⁻¹ = [...], same as the more common σ² = .... It would be a breaking change to make these into operators, and the idea was previously rejected: Unicode superscript numbers for exponentiation · Issue #9806 · JuliaLang/julia · GitHub .

7 Likes

See an article claiming that inv(A)'s bad reputation is not deserved. At the end of section 2, the author writes:

It appears that this easy-to-derive but loose bound gave the matrix inverse its bad reputation.

2 Likes

More serious is the expense, IMO.

Solving a linear system by Gaussian elimination is a constant factor 4 faster than finding the inverse [Trefethen, Numerical Linear Algebra].

3 Likes

The original author did not suggest to use inv() for solving linear systems. There are perfectly valid reasons for inverting matrices other than solving linear systems. E.g. if you need to compute its trace, or you need the inverse of a Hessian as a covariance matrix in a likelihood optimization, or a Fisher information matrix, or to compute the projection matrix on a column space, or … whatever.

Also, inv() is not a matrix-thing, it can equally well be used for things like finite fields, where its implementation can be via power operators or a generalized gcd.

Personally I do not like filling programs with utf8. It looks nice, but the next programmer may not be able to find all the funny symbols on the keyboard (like guessing that å is written \aa, which every Dane or Norwegian born before 1870 will easily guess). Or it may even be ambiguous because the glyphs look similar for two different utf8-symbols. Like m and \ttm looks different with my current font setup, but looks exactly the same with another font setup, and will of course be two different variables. And things like \epsilon in the ess julia mode in emacs is \varepsilon in the REPL.

5 Likes

I think Unicode in text files is great (when used within reason, as all things). Mostly, because it can significantly improve readability, and that is more important than writeability.

No guessing required!

help?> å
"å" can be typed by \aa<tab>

Well, that’s if you can copy-paste from the source of course. But if you’re looking at code in a picture or on paper, it doesn’t matter anyway, you can choose the symbols when writing your own code, as usual.

Totally agree. In this case Unicode actually hurts readability. That’s where “within reason” comes in. Note that this has always been a problem, not specific to Unicode symbols: maybe I and l, or O and 0 are easily distinguishable in your font but not in your collaborator’s bad font. But Unicode can definitely take this problem to a new level.

That’s a rather minor issue… Surely Emacs has a mode for writing Julia code using the same symbol names as in the REPL? In any case it is unfortunate that Emacs binds \epsilon to ϵ in the so-called “TeX input mode”.

1 Like

I am aware that both countries have outstanding life expectancy, but I would guess that the set of such people is empty in 2020. :wink:

7 Likes

If ⁻¹ is an operator, you can’t use it as part of a variable name:

X⁻¹ = inv(X)
use(X⁻¹ )
reuse(X⁻¹ )
and_again(X⁻¹ )

vs

X_inv = X⁻¹
use(X_inv)
reuse(X_inv)
and_again(X_inv)

In general I think it is more valuable to be able to use unicode super/subscripts as part of variable names, then for operator names.

3 Likes

As a side note, I learned typewriting quite some years ago, on a real typewriter. It didn’t have 0 and 1, to save mechanical costs. It was quite common at the time. We were instructed to write O and l instead. It created some unexpected problems in the computer science course the following year, it turned out that the ROM based Basic-interpreter differentiated between 0 and O, and 1 and l.

4 Likes

You can actually do this with no language changes if you’re willing to write (A)⁻¹ rather than A⁻¹. Here’s an example at the REPL:

julia> begin
           struct Inverter end
           const ⁻¹ = Inverter()
           Base.:(*)(A, ::Inverter) = inv(A)
       end

julia> A = rand(3,3)
3×3 Array{Float64,2}:
 0.51549   0.0616877  0.530502
 0.783366  0.0598045  0.697045
 0.381722  0.833891   0.632847

julia> (A)⁻¹
3×3 Array{Float64,2}:
 -13.5368    10.0475    0.280811
  -5.72129    3.08199   1.4014
  15.704    -10.1216   -0.435822

julia> ((A)⁻¹)*A ≈ I(3)
true

This works because (a)b is parsed as a * b:

julia> quote (a)b end
quote
    #= REPL[7]:1 =#
    a * b
end

so we need only define an object ⁻¹ which when multiplied by A on the left produces inv(A), trivially easy with multiple dispatch.

One could do the same with ² to make an squaring operator if they were unable to stomach reading x^2 or x*x

17 Likes

The solution is great but how can I get

to work? I get

     syntax: invalid character "²" near column 10

    Stacktrace:
    [1] top-level scope
      @ In[31]:3

while the inverter works perfectly.

2 Likes

The character ² can not be used as a variable name by itself (or as a first character of a variable name). The same thing happens to to subindex to mean x[1]. The parser would have to be changed. I don’t know what are the ramifications of such a change.

julia> ² = 10
ERROR: syntax: invalid character "²" near column 1
Stacktrace:
 [1] top-level scope
   @ none:1

julia> ₁X = 10
ERROR: syntax: invalid character "₁" near column 1
Stacktrace:
 [1] top-level scope
   @ none:1

julia> X₁ = 10
10

Paulo

1 Like

See the discussion in: allow identifiers to start with category No (Number, other) by stevengj · Pull Request #20278 · JuliaLang/julia · GitHub

1 Like

[EDIT: I first meant to respond in the thread, then decided on a private message to Mason, but posted here anyway by accident. I could delete it.]
FYI (A)⁻¹ still works in 1.10.0-beta1 (and using LinearAlgebra; ((A)⁻¹)*A ≈ I(3) which you implied and may should have written), while on my modified master:

julia> (A)⁻¹
ERROR: MethodError: no method matching inv(::Matrix{Float64})

I was going to post this to the thread and maybe file a regression issue, until I remembered my master is my experiment of (LinearAlgebra) excision, and this works if I first do using LinearAlgebra along with A*A.

I knew about A*A not working in my “Julia 2.0”, and it seemed a worthy tradeoff given my 8% if I recall faster startup. I still believe all using LinearAlgebra recovers all compatibility. In practice isn’t that what you would do in all real-world code anyway? Unless you use none of it, then of course not an issue. You can still define (multi-dimensional) arrays fine.