Is Julia lazy?

Because you are lazy, I’m making some notes to make things even easier for you :wink:

From your link:

f(M)=broadcast(x->F(x),M)
M=f([1 2 3;0 2 4;1 1 1])

Firstly, you shouldn’t write broadcast(x->F(x),M), you should just write broadcast(F,M), since x->F(x) is the same as F (except it’s even a bit worse.)

Secondly, you shouldn’t write broadcast(F,M), you should write F.(M), since that is the user-facing syntax for broadcasting.

Thirdly, you shouldn’t define f(M) at all, because broadcasting shouldn’t be hidden away like this, but be on the surface, so, you should write F.([1 2 3;0 2 4;1 1 1]) instead of f([1 2 3;0 2 4;1 1 1]).

Fourthly, you shouldn’t write F.([1 2 3;0 2 4;1 1 1]), because that allocates an unnecessary temporary array and then converts it to F, you should instead write F[1 2 3;0 2 4;1 1 1], which directly constructs the array with the correct element type :smiley:

9 Likes

very good lesson of laziness!

Corrections made according your suggestions. If you want to check maybe necessary to clean cache. I didn’t go so far as to delete naming of matrices which would save two additional lines.

I hope you’ll stop with additional simplifications so that some piece of code will be left…

Anyway to reach emptiness is total elegance. Vacuum leaves space free for your imagination.

Tomorrow is football festival (they call it soccer in US). If you’re Brit, I wish you good luck.

2 Likes

The most efficient calculation is to do nothing :wink:

Thanks, but, unfortunately, my country regrettably fell short of qualifying for the Euros, much less the final😢

It’s time for a conclusion now.
First I would like to thank all contributors, who gave to me good advises and spent time for experiments. Among them a special mention to DNF and genkuroki.
The recommendation to have a look at GaloisFields.jl was again of great value.
From the various notes I learned a lot of things about sub-typing and promotion rules and understood why my model with macro, although possible to use was not adequate.
I remind my purpose here for the moment : to teach linear algebra to students giving examples with finite fields where ‘exact’ computations are possible to illustrate the main technique of this discipline, for example computation of determinants, simplification of matrices, resolution of linear systems.
At the moment of choice I decided to adopt the Tmo Kluck’s model at least for the primary p-fields Z/pZ, it’s enough to introduce this model with the two lines :

using GaloisFields
const F = @GaloisField 5  #F will be the primary 𝔽₅=ℤ/5ℤ field

The problem now is what to do with vector spaces of finite dimension over these 𝔽p 's ? Is it advisable to continue with Dr Kluck’s model ?
First I was really impressed by the construction via a given minimal polynomial. I used this possibility in a demonstration example earlier.
I was even more impressed by such constructions :

const G, β = GaloisField(81, :β)        # degree-4 extension of ℤ/3ℤ
const G, β = GaloisField(3, 4, :β)      # same; avoid having to factorize 81

I mean without passing an irreducible polynomial as an argument. It is well known that for any n it exists an irreducible polynomial of degree n over 𝔽ₚ but I couldn’t find anywhere a fast algorithm to find one. Examples here and there use brute force together with tricks (should be unitary, divisor of X^q -X where q=p^n , shouldn’t have any root in 𝔽ₚ and all this stuff).
It seems to me that multiplication rules between elements given as linear combinations of powers of β need to know this polynomial. So the author has a trick either to find it or to bypass it. I didn’t go further in this direction but it’s very interesting.
Now Dr Kluck concentrate on the fact that such extension have a field structure, namely an algebra over 𝔽ₚ, this is too much for us; we just need to use finite powers of 𝔽ₚ with the canonical basis.
So I decided to abandon Timo’s model for vector spaces. and used instead

using Base.Iterators
using GaloisFields
const F = @GaloisField 5 #corps ℤ/5ℤ
PowerX_n(X, n) = Iterators.product(ntuple(i->X, n)...)
n=3 #puissance
P=PowerX_n(F,n) #ensembles de 3-uples
convert(t)=F[t[i] for i in 1:n]
const E=[convert(t) for t in P] #ℤ/5ℤ^3

This allows to build the space as a full list of elements or as a simple iterator by own choice.
Furthermore I decided to study vector spaces on the basic 𝔽ₚ fields only. I’m just looking for examples after all not a complete theory.
We noted that The author used indirect derivation from the basic abstract type ‘Number’ . one could hope that call for help to such beautiful tools as LinearAlgebra or AbstractAlgebra would help to compute let’s say determinants, inverses and solve systems. Alas ! it doesn’t work like genkuroki noted.
The reason is that such function use for efficiency LU decomposition and the algorithm uses such fonctions as abs, conv, adjoint, together with binary relations ‘isless’ and so on. which have meaning for real and complex fields but cannot have any equivalent with finite fields.
So since I want to use this only for educational purpose where efficiency is not a criteria I decided to write own ‘hand-made’ such functions using text-books formulas.

using GaloisFields
const F = @GaloisField 5
A=F[1 1 2;0 2 4;0 0 4]
delrowcol(M,i,j)=M[1:end .!=i,1:end .!=j] #suppression de la ième ligne et de la jème colonne
#développement suivant la première ligne
function DET(M)
    if size(M,1)==1
        return M[1,1]
    end
    s=size(M,1)
    L=[(-1)^(1+j)*M[1,j]*DET(delrowcol(M,1,j)) for j in 1:s]
    return reduce(+,L)
end

println(DET(A))

and as well

using GaloisFields
const F = @GaloisField 5
A=F[1 1 2;0 2 4;0 0 4]

delrowcol(M,i,j)=M[1:end .!=i,1:end .!=j] #suppression de la ième ligne et de la jème colonne
cofacteur(M,i,j)=(-1)^(i+j)*DET(delrowcol(M,i,j))

#développement suivant la première ligne
function DET(M)
    if size(M,1)==1
        return M[1,1]
    end
    s=size(M,1)
    L=[(-1)^(1+j)*M[1,j]*DET(delrowcol(M,1,j)) for j in 1:s]
    return reduce(+,L)
end
function cofacteurs(M)
    s=size(M,1)
    B=zeros(F,s,s)
    s=size(M,1)
    for i in 1:s ,j in 1:s
        B[i,j]=cofacteur(M,i,j)
    end
    return B
end

INV(M)=DET(M)^(-1)*transpose(cofacteurs(M))
println(DET(A))
println(INV(A))
println(A*INV(A))

I feel a little shy to send this last post because of possible reaction of DNF who estimates (Latin atavism) that I’m too much verbose.
Thank you again to you all and see you soon about affine geometry.

For what it’s worth, det works out of the box.

julia> using GaloisFields
       const F = @GaloisField 5
       A=F[1 1 2;0 2 4;0 0 4]
3×3 Matrix{𝔽₅}:
 1  1  2
 0  2  4
 0  0  4

julia> using LinearAlgebra
       det(A)
3

If we convince Julia that conj is identity, inv and system solving work as well.

julia> Base.conj(x::GaloisFields.AbstractGaloisField) = x
       inv(A)
3×3 Matrix{𝔽₅}:
 1  2  0
 0  3  2
 0  0  4

julia> A \ A[:, 1]
3-element Vector{𝔽₅}:
 1
 0
 0
1 Like

You’re completely right .
First this error with abs doesn’t appear anymore (mystery)
Second I overloaded simply conj and not Base.conj (my mistake)
Third these errors linked to undefined isless do not appear anymore (other mystery)
Thank you in any case for your checking .
Of course for somebody not used to the language.

Base.conj(x::GaloisFields.AbstractGaloisField) = x
  

looks like a magic formula.
So it must be accompanied by a long comment.
BTW

Base.conj(x::F) = x

works as well.

Since the matrix A over 𝔽₅ of GaloisFields.jl is upper triangular, LinearAlgebra.det(A) is executed successfully. But it is not the case for a generic matrix over 𝔽₅.

using GaloisFields, LinearAlgebra
const F5 = @GaloisField 5
A = F5[1 2; 0 3]
2×2 Matrix{𝔽₅}:
 1  2
 0  3
det(A)
3
B = F5[1 2; 3 4]
2×2 Matrix{𝔽₅}:
 1  2
 3  4
MethodError: no method matching abs(::𝔽₅)
......

Ref. Is Julia lazy? - #73 by genkuroki

Base.abs(x::GaloisFields.PrimeField) = x.n # type-piracy!
det(B)
3
Base.abs(x::GaloisFields.ExtensionField) = x.coeffs # type-piracy!
Base.isless(x::GaloisFields.PrimeField, y::GaloisFields.PrimeField) = isless(x.n, y.n) # type-piracy!
F9 = @GaloisField! 3 a^2 + 1
C = F9[1+a 2+a; 2+a 2+a]
2×2 Matrix{𝔽₉}:
 a + 1  a + 2
 a + 2  a + 2
det(C)
2 * a + 1

Ref. Avoid type piracy

Edit 1: minor revision.
Edit 2: add a type-piracy code.
Edit 3: add an example of ExtensionField type-piracy code.
Edit 4: add ref. for type-piracy.

Ah, there’s the difference. However, the abs codepath is really just to choose the pivot in a numerically good way, which doesn’t matter on a finite field as long as zero is avoided. So we can fool Julia to pass that hurdle with

Base.abs(x::GaloisFields.AbstractGaloisField) = x.n

Hardly a sound solution though.

I used this yesterday.
It worked for det not for inv.

If you want an easier <: Number object, use https://github.com/SimonDanisch/AbstractNumbers.jl

3 Likes
Base.conj(x::F) = x

works as well.
But only AFTER declaration of const F …

So his proposition is better ! can be placed anywhere before call to det and inv.
I made some checking

using GaloisFields
using LinearAlgebra
Base.conj(x::GaloisFields.PrimeField) = x 
Base.abs(x::GaloisFields.PrimeField) = x.n 
const F = @GaloisField 5
#M=F[1 1 2;0 2 4;1 0 3]

#println(M*inv(M))
delrowcol(M,i,j)=M[1:end .!=i,1:end .!=j] #suppression de la ième ligne et de la jème colonne

#développement suivant la première ligne
function DET(M)
    if size(M,1)==1
        return M[1,1]
    end
    s=size(M,1)
    L=[(-1)^(1+j)*M[1,j]*DET(delrowcol(M,1,j)) for j in 1:s] 
    return reduce(+,L)
end

cofacteur(M,i,j)=(-1)^(i+j)*DET(delrowcol(M,i,j))

function cofacteurs(M)
    s=size(M,1)
    B=zeros(F,s,s)
    s=size(M,1)
    for i in 1:s ,j in 1:s
        B[i,j]=cofacteur(M,i,j)
    end
    return B
end

INV(M)=DET(M)^(-1)*transpose(cofacteurs(M))

for i in 0:100
    M=F[rand(0:4) rand(0:4) rand(0:4) ; rand(0:4) rand(0:4) rand(0:4) ; rand(0:4) rand(0:4) rand(0:4)]
    d=det(M) ; D=DET(M)
    print(d);print("--"); println(D)
        if !iszero(D)
            println(inv(M)==INV(M)) 
        end
 end

Seems everything OK ! det and DET are the same inv and INV also
Maybe we can suggest the author to add these two lines in the library ?

The conj method doesn’t look too controversial. The abs method is highly sketchy and I doubt it would be accepted (I wouldn’t accept it if it was my package. :slight_smile: )

However, there’s already a recent open issue about this, either coincidental or triggered by this thread:
Use GF as matrix element type? · Issue #15 · tkluck/GaloisFields.jl · GitHub. Better chime in there.

2 Likes

I haven’t read through all this, but you might find using Mods and LinearAlgebraX to be helpful. The Mods module defines Z_p numbers and LinearAlgebraX provides exact determinants, null spaces, etc.

3 Likes

Thank you for this interesting reference. Exact computation is what is needed for educational purposes.

It’s also needed to educate people that floating-point arithmetic is what’s usually used and how that inexactness happens, and what its pitfalls are.

8 Likes

If you want a recent reference explaining all tricks about programming finite fields, look at

I actually think numerical linear algebra, with finite precision arithmetic, opens up a relatively dry topic and makes it interesting! As in, for example, the real-valued condition number is deeper, more revealing, and more important than the binary distinction between singular and nonsingular. Read Trefethen and Bau’s Numerical Linear Algebra. It’s fascinating.

5 Likes

I agree with this sentiment wholeheartedly. I found it unsatisfactory in introductory linear algebra that linear independence is a discontinuous property, as if you could somehow solve problems arising from rank deficiencies by perturbing the matrix. The singular value decomposition brings that issue (and many others) into clear focus.

Incidentally, if you want to see a presentation of the SVD with less sophistication and depth than Trefethen and Bau but brief and with mathlets and some code that you can execute in-browser, I’d like to suggest this Prismia lesson. [But grain of salt: I’m plugging my own stuff.]

5 Likes

Unfortunately if div and inv both work, rank doesn’t .
Some conversion is needed from base type to AbstractFloat ???
No idea how to overload this …
So I wrote some home-made function based on gaussean pivot .

delrowcol(M,i,j)=M[1:end .!=i,1:end .!=j] #suppression de la ième ligne et de la jème colonne
delrow(M,i)=M[1:end .!=i,1:end]
nulrow(M,i)=all((M[i,j] ==0 for j in 1:size(M,2))) 


function findpivot(M)
    L=M[1,:]
    s=size(M,1)
    for j in 1:s
        if M[1,j] !=0
            return j,M[1,j]
        end
    end
end

function rang(M)
    m,n=size(M,1),size(M,2)
    if m==1
        return nulrow(M,1) ? 0 : 1
     end
    if nulrow(M,1)
        return rang(delrow(M,1))
    end
    j,x=findpivot(M)
    N=zeros(typeof(M[1,1]),m,n)
    for i in 2:m
        N[i,:]=M[i,:]-M[1,:]*(x^(-1))*M[i,1]
    end
    return 1+rang(delrowcol(N,1,j))
end    


Any better idea ?