Skip to content

fold conj, real, imag on the symbolic imaginary unit#924

Open
oameye wants to merge 1 commit into
JuliaSymbolics:masterfrom
oameye:im-folding-rules
Open

fold conj, real, imag on the symbolic imaginary unit#924
oameye wants to merge 1 commit into
JuliaSymbolics:masterfrom
oameye:im-folding-rules

Conversation

@oameye
Copy link
Copy Markdown
Contributor

@oameye oameye commented May 18, 2026

Three new @match arms in Base.real, Base.conj, Base.imag for BasicSymbolic that match Sym(:im; type = Number) structurally and fold to 0, -im, 1.

Symbolics.IM is Sym{VartypeT}(:im; type = Number), used as a stand-in for 1im so expressions stay inside BasicSymbolic{<:Real} algebra. Without these arms, conj(IM) / real(IM) / imag(IM) were opaque wrappers that simplify could not reduce, and downstream code (e.g. SQA's qadjoint / inner_adjoint) had to special-case IM themselves to avoid conj(im) leftovers.

Matching is purely structural (name + symtype), so SymbolicUtils picks up no Symbolics-side dependency. A same-named sym with a non-Number symtype is unaffected; the test covers that case.

Add three `@match` arms in `Base.real`, `Base.conj`, `Base.imag` for
`BasicSymbolic` that match `Sym(:im; type = Number)` structurally and
fold to `0`, `-im`, and `1` respectively.

`Symbolics.IM` is defined as a `Sym{VartypeT}(:im; type = Number)` and
used as a stand-in for `1im` to keep expressions inside `BasicSymbolic{<:Real}`
algebra (where multiplying by a Julia `Complex` literal would otherwise
materialise an opaque `complex(re, im)` `Term`). Until now those three
operations on `IM` produced opaque `conj(im)` / `real(im)` / `imag(im)`
wrappers that `simplify` could not reduce, so downstream code that
algebraically conjugates `IM`-bearing expressions (e.g. SQA's
`qadjoint` / `inner_adjoint`) had to special-case `IM` themselves.

Matching is structural (name + symtype) rather than by identity, so no
new dependency on Symbolics is introduced. A same-named sym with a
non-`Number` symtype is left alone (test case).
@oameye oameye force-pushed the im-folding-rules branch from b34c98e to 8d2462b Compare May 18, 2026 13:08
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

Benchmark Results (Julia vlts)

Time benchmarks
master 8d2462b... master / 8d2462b...
arithmetic/2-arg mul 13.3 ± 0.41 μs 13.1 ± 0.39 μs 1.02 ± 0.044
arithmetic/addition 0.0821 ± 0.0016 ms 0.0808 ± 0.0073 ms 1.02 ± 0.094
arithmetic/division 26.8 ± 0.71 μs 27.5 ± 2.6 μs 0.976 ± 0.097
arithmetic/multiplication 0.0627 ± 0.0018 ms 0.0655 ± 0.0046 ms 0.958 ± 0.073
irstructure/substitute/IRSubstituter 9.74 ± 0.77 ms 9.47 ± 1.1 ms 1.03 ± 0.14
irstructure/substitute/reference 11 ± 0.95 ms 10.1 ± 0.57 ms 1.08 ± 0.11
irstructure/substitute/sparse IRSubstituter 1.45 ± 0.029 ms 1.37 ± 0.045 ms 1.06 ± 0.041
irstructure/substitute/sparse reference 2.29 ± 0.059 ms 2.17 ± 0.059 ms 1.05 ± 0.04
overhead/acrule/a+2 2.31 ± 0.17 μs 2.29 ± 0.07 μs 1.01 ± 0.08
overhead/acrule/a+2+b 0.06 ± 0 μs 0.06 ± 0 μs 1 ± 0
overhead/acrule/a+b 4.05 ± 0.32 μs 4.05 ± 0.13 μs 1 ± 0.086
overhead/acrule/noop:Int 0.04 ± 0.01 μs 0.05 ± 0.01 μs 0.8 ± 0.26
overhead/acrule/noop:Sym 0.05 ± 0.001 μs 0.05 ± 0.001 μs 1 ± 0.028
overhead/get_degrees/large_poly 0.07 ± 0.01 μs 0.07 ± 0.01 μs 1 ± 0.2
overhead/rule/noop:Int 0.06 ± 0 μs 0.06 ± 0 μs 1 ± 0
overhead/rule/noop:Sym 0.06 ± 0 μs 0.06 ± 0 μs 1 ± 0
overhead/rule/noop:Term 0.06 ± 0 μs 0.06 ± 0 μs 1 ± 0
overhead/ruleset/noop:Int 30 ± 1 ns 30 ± 0 ns 1 ± 0.033
overhead/ruleset/noop:Sym 0.261 ± 0.001 μs 0.27 ± 0.011 μs 0.967 ± 0.04
overhead/ruleset/noop:Term 1.12 ± 0.02 μs 1.08 ± 0.01 μs 1.04 ± 0.021
overhead/simplify/noop:Int 30 ± 0 ns 30 ± 0 ns 1 ± 0
overhead/simplify/noop:Sym 30 ± 10 ns 30 ± 10 ns 1 ± 0.47
overhead/simplify/noop:Term 27.3 ± 1 μs 27.7 ± 0.64 μs 0.985 ± 0.044
overhead/simplify/randterm (+, *):serial 0.257 ± 0.0023 s 0.265 ± 0.023 s 0.97 ± 0.083
overhead/simplify/randterm (+, *):thread 0.296 ± 0.017 s 0.285 ± 0.024 s 1.04 ± 0.11
overhead/simplify/randterm (/, *):serial 0.165 ± 0.011 ms 0.169 ± 0.022 ms 0.975 ± 0.14
overhead/simplify/randterm (/, *):thread 0.17 ± 0.012 ms 0.175 ± 0.02 ms 0.975 ± 0.13
overhead/substitute/a 0.0432 ± 0.0011 ms 0.0435 ± 0.0012 ms 0.992 ± 0.037
overhead/substitute/a,b 0.0513 ± 0.0011 ms 0.0523 ± 0.0015 ms 0.982 ± 0.036
overhead/substitute/a,b,c 0.0457 ± 0.0013 ms 0.0486 ± 0.0014 ms 0.941 ± 0.038
polyform/easy_iszero 23.1 ± 0.41 μs 23.3 ± 0.68 μs 0.991 ± 0.034
polyform/isone 1.1 ± 0.036 ms 1.1 ± 0.081 ms 0.997 ± 0.08
polyform/isone:noop 0.08 ± 0.01 μs 0.08 ± 0.01 μs 1 ± 0.18
polyform/iszero 0.943 ± 0.03 ms 0.938 ± 0.067 ms 1 ± 0.079
polyform/iszero:noop 0.08 ± 0.01 μs 0.101 ± 0.01 μs 0.792 ± 0.13
polyform/simplify_fractions 1.19 ± 0.038 ms 1.2 ± 0.075 ms 0.992 ± 0.07
printing/large_poly 0.217 ± 0.0047 s 0.212 ± 0.0052 s 1.02 ± 0.033
time_to_load 1.57 ± 0.021 s 1.58 ± 0.018 s 0.991 ± 0.017
Memory benchmarks
master 8d2462b... master / 8d2462b...
arithmetic/2-arg mul 0.078 k allocs: 2.72 kB 0.078 k allocs: 2.72 kB 1
arithmetic/addition 0.468 k allocs: 16.9 kB 0.438 k allocs: 16 kB 1.06
arithmetic/division 0.142 k allocs: 5.47 kB 0.142 k allocs: 5.47 kB 1
arithmetic/multiplication 0.356 k allocs: 11.7 kB 0.356 k allocs: 11.7 kB 1
irstructure/substitute/IRSubstituter 0.0438 M allocs: 1.64 MB 0.0396 M allocs: 1.45 MB 1.13
irstructure/substitute/reference 0.0452 M allocs: 1.66 MB 0.0452 M allocs: 1.66 MB 1
irstructure/substitute/sparse IRSubstituter 5.17 k allocs: 0.196 MB 4.7 k allocs: 0.174 MB 1.13
irstructure/substitute/sparse reference 10 k allocs: 0.381 MB 10 k allocs: 0.381 MB 1
overhead/acrule/a+2 0.034 k allocs: 1.25 kB 0.034 k allocs: 1.25 kB 1
overhead/acrule/a+2+b 0 allocs: 0 B 0 allocs: 0 B
overhead/acrule/a+b 0.047 k allocs: 1.8 kB 0.047 k allocs: 1.8 kB 1
overhead/acrule/noop:Int 0 allocs: 0 B 0 allocs: 0 B
overhead/acrule/noop:Sym 0 allocs: 0 B 0 allocs: 0 B
overhead/get_degrees/large_poly 2 allocs: 32 B 2 allocs: 32 B 1
overhead/rule/noop:Int 2 allocs: 0.0625 kB 2 allocs: 0.0625 kB 1
overhead/rule/noop:Sym 2 allocs: 0.0625 kB 2 allocs: 0.0625 kB 1
overhead/rule/noop:Term 2 allocs: 0.0625 kB 2 allocs: 0.0625 kB 1
overhead/ruleset/noop:Int 0 allocs: 0 B 0 allocs: 0 B
overhead/ruleset/noop:Sym 3 allocs: 0.109 kB 3 allocs: 0.109 kB 1
overhead/ruleset/noop:Term 12 allocs: 0.391 kB 12 allocs: 0.391 kB 1
overhead/simplify/noop:Int 0 allocs: 0 B 0 allocs: 0 B
overhead/simplify/noop:Sym 0 allocs: 0 B 0 allocs: 0 B
overhead/simplify/noop:Term 0.298 k allocs: 11.5 kB 0.298 k allocs: 11.5 kB 1
overhead/simplify/randterm (+, *):serial 2.55 M allocs: 0.0974 GB 2.53 M allocs: 0.0972 GB 1
overhead/simplify/randterm (+, *):thread 2.59 M allocs: 0.255 GB 2.59 M allocs: 0.255 GB 1
overhead/simplify/randterm (/, *):serial 2.04 k allocs: 0.0733 MB 2.04 k allocs: 0.0733 MB 1
overhead/simplify/randterm (/, *):thread 2.07 k allocs: 0.0743 MB 2.07 k allocs: 0.0743 MB 1
overhead/substitute/a 0.233 k allocs: 8.62 kB 0.22 k allocs: 8.42 kB 1.02
overhead/substitute/a,b 0.28 k allocs: 10.3 kB 0.267 k allocs: 10.1 kB 1.02
overhead/substitute/a,b,c 0.25 k allocs: 8.8 kB 0.238 k allocs: 8.62 kB 1.02
polyform/easy_iszero 0.133 k allocs: 4.58 kB 0.133 k allocs: 4.58 kB 1
polyform/isone 8.28 k allocs: 0.569 MB 8.33 k allocs: 0.573 MB 0.992
polyform/isone:noop 1 allocs: 16 B 1 allocs: 16 B 1
polyform/iszero 6.86 k allocs: 0.474 MB 6.86 k allocs: 0.474 MB 1
polyform/iszero:noop 1 allocs: 16 B 1 allocs: 16 B 1
polyform/simplify_fractions 8.76 k allocs: 0.592 MB 8.81 k allocs: 0.596 MB 0.993
printing/large_poly 1.86 M allocs: 0.082 GB 1.86 M allocs: 0.082 GB 1
time_to_load 0.153 k allocs: 14.5 kB 0.153 k allocs: 14.5 kB 1

@github-actions
Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
master 8d2462b... master / 8d2462b...
arithmetic/2-arg mul 10 ± 0.27 μs 10.2 ± 0.29 μs 0.981 ± 0.038
arithmetic/addition 0.0701 ± 0.0013 ms 0.0681 ± 0.0011 ms 1.03 ± 0.025
arithmetic/division 22 ± 0.49 μs 21.9 ± 0.49 μs 1.01 ± 0.032
arithmetic/multiplication 0.0489 ± 0.0015 ms 0.0516 ± 0.0022 ms 0.948 ± 0.051
irstructure/substitute/IRSubstituter 7.56 ± 0.17 ms 7.6 ± 0.16 ms 0.995 ± 0.031
irstructure/substitute/reference 7.82 ± 0.21 ms 7.97 ± 0.39 ms 0.981 ± 0.055
irstructure/substitute/sparse IRSubstituter 1.2 ± 0.032 ms 1.13 ± 0.031 ms 1.06 ± 0.041
irstructure/substitute/sparse reference 1.66 ± 0.033 ms 1.72 ± 0.12 ms 0.968 ± 0.069
overhead/acrule/a+2 2.25 ± 0.11 μs 2.21 ± 0.11 μs 1.02 ± 0.071
overhead/acrule/a+2+b 0.08 ± 0.01 μs 0.08 ± 0.01 μs 1 ± 0.18
overhead/acrule/a+b 3.92 ± 0.17 μs 3.79 ± 0.16 μs 1.03 ± 0.063
overhead/acrule/noop:Int 30 ± 0 ns 30 ± 0 ns 1 ± 0
overhead/acrule/noop:Sym 0.06 ± 0 μs 0.06 ± 0 μs 1 ± 0
overhead/get_degrees/large_poly 0.071 ± 0.01 μs 0.08 ± 0.01 μs 0.887 ± 0.17
overhead/rule/noop:Int 0.07 ± 0.01 μs 0.07 ± 0.01 μs 1 ± 0.2
overhead/rule/noop:Sym 0.07 ± 0.01 μs 0.07 ± 0.01 μs 1 ± 0.2
overhead/rule/noop:Term 0.07 ± 0.01 μs 0.07 ± 0.01 μs 1 ± 0.2
overhead/ruleset/noop:Int 30 ± 0 ns 30 ± 0 ns 1 ± 0
overhead/ruleset/noop:Sym 0.271 ± 0.01 μs 0.26 ± 0.001 μs 1.04 ± 0.039
overhead/ruleset/noop:Term 1.05 ± 0.039 μs 1.05 ± 0.03 μs 0.999 ± 0.047
overhead/simplify/noop:Int 30 ± 0 ns 30 ± 0 ns 1 ± 0
overhead/simplify/noop:Sym 0.04 ± 0.01 μs 0.04 ± 0.01 μs 1 ± 0.35
overhead/simplify/noop:Term 29.3 ± 0.7 μs 25.2 ± 0.57 μs 1.16 ± 0.038
overhead/simplify/randterm (+, *):serial 0.237 ± 0.044 s 0.229 ± 0.031 s 1.04 ± 0.24
overhead/simplify/randterm (+, *):thread 0.286 ± 0.28 s 0.274 ± 0.063 s 1.04 ± 1
overhead/simplify/randterm (/, *):serial 0.182 ± 0.015 ms 0.16 ± 0.024 ms 1.14 ± 0.2
overhead/simplify/randterm (/, *):thread 0.195 ± 0.027 ms 0.177 ± 0.038 ms 1.1 ± 0.28
overhead/substitute/a 31.4 ± 0.72 μs 31.4 ± 0.6 μs 0.999 ± 0.03
overhead/substitute/a,b 0.0388 ± 0.00085 ms 0.0386 ± 0.00071 ms 1 ± 0.029
overhead/substitute/a,b,c 0.0387 ± 0.00084 ms 0.0369 ± 0.00075 ms 1.05 ± 0.031
polyform/easy_iszero 18 ± 0.39 μs 18.4 ± 0.44 μs 0.983 ± 0.032
polyform/isone 0.868 ± 0.021 ms 0.865 ± 0.025 ms 1 ± 0.038
polyform/isone:noop 0.08 ± 0 μs 0.08 ± 0 μs 1 ± 0
polyform/iszero 0.747 ± 0.017 ms 0.74 ± 0.019 ms 1.01 ± 0.035
polyform/iszero:noop 0.08 ± 0 μs 0.08 ± 0.001 μs 1 ± 0.013
polyform/simplify_fractions 0.941 ± 0.019 ms 0.94 ± 0.076 ms 1 ± 0.084
printing/large_poly 0.21 ± 0.027 s 0.206 ± 0.02 s 1.02 ± 0.16
time_to_load 1.63 ± 0.012 s 1.67 ± 0.011 s 0.975 ± 0.0099
Memory benchmarks
master 8d2462b... master / 8d2462b...
arithmetic/2-arg mul 0.056 k allocs: 1.78 kB 0.056 k allocs: 1.78 kB 1
arithmetic/addition 0.33 k allocs: 11.3 kB 0.3 k allocs: 10.3 kB 1.09
arithmetic/division 0.132 k allocs: 4.77 kB 0.132 k allocs: 4.77 kB 1
arithmetic/multiplication 0.252 k allocs: 6.5 kB 0.252 k allocs: 6.5 kB 1
irstructure/substitute/IRSubstituter 0.0327 M allocs: 1.06 MB 0.0324 M allocs: 1.05 MB 1.01
irstructure/substitute/reference 0.0353 M allocs: 1.23 MB 0.0353 M allocs: 1.23 MB 1
irstructure/substitute/sparse IRSubstituter 3.81 k allocs: 0.126 MB 3.81 k allocs: 0.125 MB 1
irstructure/substitute/sparse reference 6.49 k allocs: 0.293 MB 6.49 k allocs: 0.293 MB 1
overhead/acrule/a+2 0.034 k allocs: 1.12 kB 0.034 k allocs: 1.12 kB 1
overhead/acrule/a+2+b 0 allocs: 0 B 0 allocs: 0 B
overhead/acrule/a+b 0.046 k allocs: 1.55 kB 0.046 k allocs: 1.55 kB 1
overhead/acrule/noop:Int 0 allocs: 0 B 0 allocs: 0 B
overhead/acrule/noop:Sym 0 allocs: 0 B 0 allocs: 0 B
overhead/get_degrees/large_poly 2 allocs: 32 B 2 allocs: 32 B 1
overhead/rule/noop:Int 2 allocs: 0.0625 kB 2 allocs: 0.0625 kB 1
overhead/rule/noop:Sym 2 allocs: 0.0625 kB 2 allocs: 0.0625 kB 1
overhead/rule/noop:Term 2 allocs: 0.0625 kB 2 allocs: 0.0625 kB 1
overhead/ruleset/noop:Int 0 allocs: 0 B 0 allocs: 0 B
overhead/ruleset/noop:Sym 3 allocs: 0.109 kB 3 allocs: 0.109 kB 1
overhead/ruleset/noop:Term 12 allocs: 0.391 kB 12 allocs: 0.391 kB 1
overhead/simplify/noop:Int 0 allocs: 0 B 0 allocs: 0 B
overhead/simplify/noop:Sym 0 allocs: 0 B 0 allocs: 0 B
overhead/simplify/noop:Term 0.284 k allocs: 9.91 kB 0.284 k allocs: 9.91 kB 1
overhead/simplify/randterm (+, *):serial 2.39 M allocs: 0.0829 GB 2.39 M allocs: 0.083 GB 0.999
overhead/simplify/randterm (+, *):thread 2.55 M allocs: 0.245 GB 2.55 M allocs: 0.245 GB 1
overhead/simplify/randterm (/, *):serial 1.91 k allocs: 0.0655 MB 1.91 k allocs: 0.0655 MB 1
overhead/simplify/randterm (/, *):thread 2.05 k allocs: 0.0704 MB 2.05 k allocs: 0.0704 MB 1
overhead/substitute/a 0.172 k allocs: 6.05 kB 0.172 k allocs: 6.05 kB 1
overhead/substitute/a,b 0.223 k allocs: 7.69 kB 0.223 k allocs: 7.69 kB 1
overhead/substitute/a,b,c 0.229 k allocs: 7.81 kB 0.229 k allocs: 7.81 kB 1
polyform/easy_iszero 0.092 k allocs: 2.94 kB 0.092 k allocs: 2.94 kB 1
polyform/isone 10.9 k allocs: 0.574 MB 10.9 k allocs: 0.578 MB 0.993
polyform/isone:noop 1 allocs: 16 B 1 allocs: 16 B 1
polyform/iszero 8.96 k allocs: 0.48 MB 8.96 k allocs: 0.48 MB 1
polyform/iszero:noop 1 allocs: 16 B 1 allocs: 16 B 1
polyform/simplify_fractions 11.3 k allocs: 0.591 MB 11.4 k allocs: 0.595 MB 0.993
printing/large_poly 2.15 M allocs: 0.079 GB 2.15 M allocs: 0.079 GB 1
time_to_load 0.145 k allocs: 11 kB 0.145 k allocs: 11 kB 1

@AayushSabharwal
Copy link
Copy Markdown
Member

Thanks for your attention to these edge cases. I think the better way to phrase this is to move the const IM here, and compare against that in the functions. Otherwise, SymbolicUtils is effectively relying on a convention introduced in Symbolics.jl, when the dependency should be the other way around.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants