Skip to content

* on matrices of custom number types used to work in Julia <=1.10 and does not anymore (now requires zero to be defined and maybe shouldn't) #1486

@jmichel7

Description

@jmichel7

The following (minimum definition of addition and multiplication of modular numbers) enabled to multiply matrices in Julia 1.0 to 1.10 (and probably before, I was not using Julia then)

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.10.9 (2025-03-10)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> struct Mod<:Number
         val::Int
         modulo::Int
         Mod(x::Int,y::Int)=new(x%y,y)
       end

julia> Base.:+(x::Mod, y::Mod)=Mod(x.val+y.val,x.modulo)

julia> Base.:*(x::Mod, y::Mod)=Mod(x.val*y.val,x.modulo)

julia> m=Mod.([4 5;5 4],20)
2×2 Matrix{Mod}:
 Mod(4, 20)  Mod(5, 20)
 Mod(5, 20)  Mod(4, 20)

julia> m*m
2×2 Matrix{Mod}:
 Mod(1, 20)  Mod(0, 20)
 Mod(0, 20)  Mod(1, 20)

but in julia 11 and 12 we get

julia> m*m
ERROR: MethodError: no method matching Mod(::Int64)
The type `Mod` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
  Mod(::Int64, ::Int64)
   @ Main REPL[1]:4
  (::Type{T})(::T) where T<:Number
   @ Core boot.jl:965
  (::Type{T})(::Base.TwicePrecision) where T<:Number
   @ Base twiceprecision.jl:265
  ...

Stacktrace:
  [1] convert(::Type{Mod}, x::Int64)
    @ Base ./number.jl:7
  [2] zero(::Type{Mod})
    @ Base ./number.jl:314
  [3] _rmul_or_fill!
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/generic.jl:169 [inlined]
  [4] _generic_matmatmul_nonadjtrans!
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:1031 [inlined]
  [5] __generic_matmatmul!
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:1027 [inlined]
  [6] _generic_matmatmul!(C::Matrix{Mod}, A::Matrix{Mod}, B::Matrix{Mod}, alpha::Bool, beta::Bool)
    @ LinearAlgebra ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:1021
  [7] generic_matmatmul!
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:1011 [inlined]
  [8] generic_matmatmul_wrapper!
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:343 [inlined]
  [9] _mul!
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:328 [inlined]
 [10] mul!
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:297 [inlined]
 [11] mul!
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:265 [inlined]
 [12] mul
    @ ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:118 [inlined]
 [13] *(A::Matrix{Mod}, B::Matrix{Mod})
    @ LinearAlgebra ~/julia-1.12.1/share/julia/stdlib/v1.12/LinearAlgebra/src/matmul.jl:114
 [14] top-level scope
    @ REPL[5]:1

The problem is the call zero(Mod), which should not be necessary to multiply two matrices. We can define

Base.zero(x::Mod)=Mod(0,x.modulo)

but not zero(Mod) since the type Mod does not contain the information modulo. I find this a serious regression in the generality of Julia. In mathematics, there are lots of similar numbers where zero(x) makes sense but not zero(typeof(x)), and Julia handles recently worse and worse this type of numbers (I found also that LinearAlgebra.det_bareiss(m), which used to work, does not anymore).

I think it is would be worth taking a minimum of care writing generic code in Linear algebra so more of it works with such objects. For example, one could use instead of zero(eltype(m)) a function

zeroentry(m)=isempty(m) ? zero(eltype(m)) : zero(first(m))

which would limit the problem to empty matrices.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions