matrixjuliasymbolic-math

Why doesn't Symbolics.expand work in matrices?


I'm working with Julia's Symbolics.jl and I'm trying to compare two symbolic matrices.

However, since Symbolics.isequal function checks for structural- instead of mathematical-equivalence it is necessary that every entry of the matrix is in its expanded form. Otherwise isequal will always return false.

To ensure this I made the function tidyMatrix!(M), here's a minimal (non-working) example:

 using Symbolics
 
 # Function to expand every symbolic entry in matrix M
 function tidyMatrix!(M)
    for i in size(M, 1), j in size(M, 2)
        M[i,j] = expand(M[i,j])
    end
end

 @variables x
 M1 = [2(exp(x) + 1) 0; 0 0]
 M2 = [2*exp(x)+2 0; 0 0]

tidyMatrix!(M1)
tidyMatrix!(M2)

println("Symbolic isequal: ", Symbolics.isequal(M1, M2)) # Always returns false

But tidyfunction! never manages to expand the entries in M1.

Which is especially weird since the expand function is working as expected on single entries, like here:

 using Symbolics
 
 # Function to expand every symbolic entry in matrix M
 function tidyMatrix!(M)
    for i in size(M, 1), j in size(M, 2)
        M[i,j] = expand(M[i,j])
    end
end

 @variables x
 M1 = [2(exp(x) + 1)] # only use one entry
 M2 = [2*exp(x)+2]

tidyMatrix!(M1)
tidyMatrix!(M2)

println("Symbolic isequal: ", Symbolics.isequal(M1, M2)) # Now it works

I know I could just check for numerical equality, but after I've sunken hours into indentifying this bug I just need to know why the expand function breaks assoon as I use it elementwise in a matrix.


Appendix: If you swap the expand function for similar functions like simplify, simplify(..., expand=true) it still doesn't make tidyfunction work.


Solution

  • Your ranges are written incorrectly. You want for i in 1:size(M, 1), j in 1:size(M, 2). When iterating over a scalar (which is what size returns), it's treated as a 0-dimensional array containing just itself. So you only expanded the bottom right entry of the array (which is why it worked in the one-element case).

    Actually if you really want to be correct, you shouldn't use the range 1:n but rather let the array tell you its own indices (in case you use something like an OffsetArray):

    function tidyMatrix!(M)
       for i in eachindex(M)
           M[i] = expand(M[i])
       end
    end