tkluck / GaloisFields.jl

Finite fields for Julia
Other
47 stars 6 forks source link

Use GF as matrix element type? #15

Open infogulch opened 3 years ago

infogulch commented 3 years ago

Hi! I'm trying to use GaloisFields as an element type in matrices, but I'm running into MethodErrors when using some of the stdlib matrix functions, some examples:

using GaloisFields
using LinearAlgebra
const F = @GaloisField β„€/257β„€

x = convert(Matrix{F},reshape(rand(F,4),2,2)) # random elements

det(x)   # MethodError: no method matching abs(::𝔽₂₅₇)
inv(x)   # MethodError: no method matching abs(::𝔽₂₅₇)
conj(x)  # MethodError: no method matching conj(::𝔽₂₅₇)

y = convert(Matrix{F},[1 0; 0 1]) # identity matrix elements

det(y)    # ok, returns 1
inv(y)    # MethodError: no method matching abs(::𝔽₂₅₇)
conj(y)   # MethodError: no method matching abs(::𝔽₂₅₇)

I did try the obvious defining abs:

abs(x::F) = x

But that didn't have any effect.

The stack trace of one of the abs errors is:

MethodError: no method matching abs(::𝔽₂₅₇)
Closest candidates are:
  abs(::Bool) at bool.jl:79
  abs(::Unsigned) at int.jl:169
  abs(::Signed) at int.jl:170
  ...

Stacktrace:
 [1] generic_lufact!(A::Matrix{𝔽₂₅₇}, ::Val{true}; check::Bool)
   @ LinearAlgebra /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/LinearAlgebra/src/lu.jl:143
 [2] #lu!#134
   @ /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/LinearAlgebra/src/lu.jl:130 [inlined]
 [3] #lu#136
   @ /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/LinearAlgebra/src/lu.jl:273 [inlined]
 [4] det(A::Matrix{𝔽₂₅₇})
   @ LinearAlgebra /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/LinearAlgebra/src/generic.jl:1560

And for conj:

MethodError: no method matching conj(::𝔽₂₅₇)
Closest candidates are:
  conj(::Union{Hermitian{T, S}, Symmetric{T, S}} where {T, S}) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/LinearAlgebra/src/symmetric.jl:368
  conj(::P) where P<:Polynomials.LaurentPolynomial at /opt/julia/packages/Polynomials/1aa8e/src/polynomials/LaurentPolynomial.jl:327
  conj(::P) where P<:Polynomials.AbstractPolynomial at /opt/julia/packages/Polynomials/1aa8e/src/common.jl:301
  ...

I must admit I'm new to both Julia and GF, so it's quite possible I'm doing something wrong. πŸ˜…

Thoughts?

tkluck commented 3 years ago

For now the best solution is to use LinearAlgebraX.jl for this use case.

I'll leave this bug open; maybe we can depend on LinearAlgebraX and add implementations like det(a::AbstractMatrix{<:GaloisField}) = detx(a).

I did try the obvious defining abs:

abs(x::F) = x

But that didn't have any effect.

FWIW for adding methods to functions from Base you have to qualify them:

Base.abs(x::F) = x

But in this case it will just complain about other methods next.

infogulch commented 3 years ago

Wonderful, thank you for the direction and pointers!

maybe we can depend on LinearAlgebraX and add implementations

That sounds like it would be neat; though I might worry about adding a dependency for users that don't use that feature. I'm not sure I've ever encountered this in a software engineering context, multiple dispatch adds a lot of new capabilities to tie libraries together, but also brings some new problems to solve. I wonder if the community has a general strategy for this yet.

Perhaps a simple mention in the readme would be sufficient:

If you want to use GaloisFields with matrices look at the LinearAlgebraX ("Exact linear algebra functions") package.


So I tried to use LinearAlgebraX, but I ran into an issue where the result of the vector numeric equality binary operation results an array of GaloisField values instead of booleans:

using GaloisFields, LinearAlgebraX
const F = @GaloisField β„€/257β„€

print(F(10) == 0)         # "false" -- ok
print([F(10) F(8)] .== 0) # 𝔽₂₅₇[0 0] -- ??? I expect [false false] instead
all([F(10) F(8)] .== 0)   # TypeError: non-boolean (𝔽₂₅₇) used in boolean context

This is used in LinearAlgebraX as part of the implementation of invx.

Any ideas why that happens?

infogulch commented 3 years ago

Oh interesting, using the extended GF form works fine:

const G = @GaloisField! 2^8 Ξ²

print([Ξ²^10 Ξ²^8] .== 0)
all([Ξ²^10 Ξ²^8] .== 0)

Bool[0 0] false

tkluck commented 3 years ago

@infogulch Thanks for reporting the bug about broadcasting ==. This is a bug in GaloisFields.jl and I'm opening #16 for you.

infogulch commented 3 years ago

Great, thanks! If I find something else I'll put it in a new issue next time. πŸ˜„

Gilles-Paul-Dubois commented 3 years ago

This subject discussed here as well