Skip to content

Commit

Permalink
fix: fix hashconsing not comparing metadata of symbolics inside metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
AayushSabharwal committed Jan 10, 2025
1 parent 982e6b1 commit d2285a0
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 2 deletions.
55 changes: 54 additions & 1 deletion src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,60 @@ downstream packages like `ModelingToolkit.jl`, hence the need for this separate
function.
"""
function isequal_with_metadata(a::BasicSymbolic, b::BasicSymbolic)::Bool
isequal(a, b) && isequal(metadata(a), metadata(b))
isequal(a, b) && isequal_with_metadata(metadata(a), metadata(b))
end

"""
$(TYPEDSIGNATURES)
Compare the metadata of two `BasicSymbolic`s to ensure it is equal, recursively calling
`isequal_with_metadata` to ensure symbolic variables in the metadata also have equal
metadata.
"""
function isequal_with_metadata(a::Union{AbstractDict, Tuple, NamedTuple}, b::Union{AbstractDict, Tuple, NamedTuple})
typeof(a) == typeof(b) || return false
length(a) == length(b) || return false

for (k, v) in pairs(a)
haskey(b, k) || return false
isequal_with_metadata(v, b[k]) || return false
end

for (k, v) in pairs(b)
haskey(a, k) || return false
isequal_with_metadata(v, a[k]) || return false
end

return true
end

"""
$(TYPEDSIGNATURES)
Fallback method which uses `isequal`.
"""
isequal_with_metadata(a, b) = isequal(a, b)

"""
$(TYPEDSIGNATURES)
Specialized methods to check if two ranges are equal without comparing each element.
"""
isequal_with_metadata(a::AbstractRange, b::AbstractRange) = isequal(a, b)

"""
$(TYPEDSIGNATURES)
Check if two arrays are equal by calling `isequal_with_metadata` on each element. This
is to ensure true equality of any symbolic elements, if present.
"""
function isequal_with_metadata(a::AbstractArray, b::AbstractArray)
typeof(a) == typeof(b) || return false
size(a) == size(b) || return false
for (x, y) in zip(a, b)
isequal_with_metadata(x, y) || return false
end
return true
end

Base.one( s::Symbolic) = one( symtype(s))
Expand Down
13 changes: 12 additions & 1 deletion test/hash_consing.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using SymbolicUtils, Test
using SymbolicUtils: Term, Add, Mul, Div, Pow, hash2
using SymbolicUtils: Term, Add, Mul, Div, Pow, hash2, metadata

struct Ctx1 end
struct Ctx2 end
Expand Down Expand Up @@ -108,3 +108,14 @@ end
@test hash2(f, u0) != hash2(r, u0)
@test f + a !== r + a
end

@testset "Symbolics in metadata" begin
@syms a b
a1 = setmetadata(a, Int, b)
b1 = setmetadata(b, Int, 3)
a2 = setmetadata(a, Int, b1)
@test a1 !== a2
@test !SymbolicUtils.isequal_with_metadata(a1, a2)
@test metadata(metadata(a1)[Int]) === nothing
@test metadata(metadata(a2)[Int])[Int] == 3
end

0 comments on commit d2285a0

Please sign in to comment.