@@ -23,39 +23,39 @@ const EMPTY_DICT_T = typeof(EMPTY_DICT)
2323const ENABLE_HASHCONSING = Ref (true )
2424
2525@compactify show_methods= false begin
26- @abstract mutable struct BasicSymbolic{T} <: Symbolic{T}
26+ @abstract struct BasicSymbolic{T} <: Symbolic{T}
2727 metadata:: Metadata = NO_METADATA
2828 end
29- mutable struct Sym{T} <: BasicSymbolic{T}
29+ struct Sym{T} <: BasicSymbolic{T}
3030 name:: Symbol = :OOF
3131 end
32- mutable struct Term{T} <: BasicSymbolic{T}
32+ struct Term{T} <: BasicSymbolic{T}
3333 f:: Any = identity # base/num if Pow; issorted if Add/Dict
3434 arguments:: Vector{Any} = EMPTY_ARGS
3535 hash:: RefValue{UInt} = EMPTY_HASH
3636 hash2:: RefValue{UInt} = EMPTY_HASH
3737 end
38- mutable struct Mul{T} <: BasicSymbolic{T}
38+ struct Mul{T} <: BasicSymbolic{T}
3939 coeff:: Any = 0 # exp/den if Pow
4040 dict:: EMPTY_DICT_T = EMPTY_DICT
4141 hash:: RefValue{UInt} = EMPTY_HASH
4242 hash2:: RefValue{UInt} = EMPTY_HASH
4343 arguments:: Vector{Any} = EMPTY_ARGS
4444 end
45- mutable struct Add{T} <: BasicSymbolic{T}
45+ struct Add{T} <: BasicSymbolic{T}
4646 coeff:: Any = 0 # exp/den if Pow
4747 dict:: EMPTY_DICT_T = EMPTY_DICT
4848 hash:: RefValue{UInt} = EMPTY_HASH
4949 hash2:: RefValue{UInt} = EMPTY_HASH
5050 arguments:: Vector{Any} = EMPTY_ARGS
5151 end
52- mutable struct Div{T} <: BasicSymbolic{T}
52+ struct Div{T} <: BasicSymbolic{T}
5353 num:: Any = 1
5454 den:: Any = 1
5555 simplified:: Bool = false
5656 arguments:: Vector{Any} = EMPTY_ARGS
5757 end
58- mutable struct Pow{T} <: BasicSymbolic{T}
58+ struct Pow{T} <: BasicSymbolic{T}
5959 base:: Any = 1
6060 exp:: Any = 1
6161 arguments:: Vector{Any} = EMPTY_ARGS
@@ -86,7 +86,15 @@ function exprtype(x::BasicSymbolic)
8686 end
8787end
8888
89- const wvd = TaskLocalValue {WeakValueDict{UInt, BasicSymbolic}} (WeakValueDict{UInt, BasicSymbolic})
89+ mutable struct HashConsingWrapper
90+ bs:: BasicSymbolic
91+ end
92+
93+ Base. hash (x:: HashConsingWrapper , h:: UInt ) = hash2 (x. bs, h)
94+
95+ Base. isequal (x:: HashConsingWrapper , y:: HashConsingWrapper ) = isequal_with_metadata (x. bs, y. bs)
96+
97+ const wkd = TaskLocalValue {WeakKeyDict{HashConsingWrapper, Nothing}} (WeakKeyDict{HashConsingWrapper, Nothing})
9098
9199# Same but different error messages
92100@noinline error_on_type () = error (" Internal error: unreachable reached!" )
@@ -522,15 +530,15 @@ Implements hash consing (flyweight design pattern) for `BasicSymbolic` objects.
522530
523531This function checks if an equivalent `BasicSymbolic` object already exists. It uses a
524532custom hash function (`hash2`) incorporating metadata and symtypes to search for existing
525- objects in a `WeakValueDict ` (`wvd `). Due to the possibility of hash collisions (where
533+ objects in a `WeakKeyDict ` (`wkd `). Due to the possibility of hash collisions (where
526534different objects produce the same hash), a custom equality check (`isequal_with_metadata`)
527535which includes metadata comparison, is used to confirm the equivalence of objects with
528536matching hashes. If an equivalent object is found, the existing object is returned;
529537otherwise, the input `s` is returned. This reduces memory usage, improves compilation time
530538for runtime code generation, and supports built-in common subexpression elimination,
531539particularly when working with symbolic objects with metadata.
532540
533- Using a `WeakValueDict ` ensures that only weak references to `BasicSymbolic` objects are
541+ Using a `WeakKeyDict ` ensures that only weak references to `BasicSymbolic` objects are
534542stored, allowing objects that are no longer strongly referenced to be garbage collected.
535543Custom functions `hash2` and `isequal_with_metadata` are used instead of `Base.hash` and
536544`Base.isequal` to accommodate metadata without disrupting existing tests reliant on the
@@ -540,12 +548,14 @@ function BasicSymbolic(s::BasicSymbolic)::BasicSymbolic
540548 if ! ENABLE_HASHCONSING[]
541549 return s
542550 end
543- h = hash2 (s)
544- t = get! (wvd[], h, s)
545- if t === s || isequal_with_metadata (t, s)
546- t
551+ cache = wkd[]
552+ hcw = HashConsingWrapper (s)
553+ k = getkey (cache, hcw, nothing )
554+ if isnothing (k)
555+ cache[hcw] = nothing
556+ return s
547557 else
548- s
558+ return k . bs
549559 end
550560end
551561
0 commit comments