Skip to content

fix: hash symbolic quantities structurally#213

Merged
MilesCranmer merged 6 commits into
JuliaPhysics:mainfrom
MilesCranmerBot:fix/issue-212
Apr 22, 2026
Merged

fix: hash symbolic quantities structurally#213
MilesCranmer merged 6 commits into
JuliaPhysics:mainfrom
MilesCranmerBot:fix/issue-212

Conversation

@MilesCranmerBot

@MilesCranmerBot MilesCranmerBot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix inconsistent hashing for symbolic dimensions and symbolic quantities so equal symbolic units deduplicate correctly.

Changes

  • add structural hash for AbstractSymbolicDimensions
  • add structural hash for quantities based on value plus dimensions
  • add regression tests for symbolic-unit deduplication and hash stability

Testing

  • /usr/local/bin/julia --project -e 'using DynamicQuantities, Test; @register_unit gacc 9.81u"m/s^2"; @test hash(SymbolicDimensions(; m=1, s=-1)) == hash(SymbolicDimensions(; m=1, g=0, s=-1)); @test hash(us"m/s") == hash(us"m/s"); @test length(unique(fill(us"m/s", 10))) == 1; @test length(unique(sym_uparse.(fill("gacc",10)))) == 1; println("targeted regression checks passed")'

Related to #212

Fix inconsistent hashing for symbolic dimensions and quantities so equal symbolic units deduplicate correctly.

Fixes JuliaPhysics#212

Co-authored-by: Miles Cranmer <miles.cranmer@gmail.com>
@github-actions

github-actions Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1.10)

Time benchmarks
main 4b2d1f3... main / 4b2d1f3...
Quantity/creation/Quantity(x) 3.41 ± 0.001 ns 3.41 ± 0.01 ns 1 ± 0.0029
Quantity/creation/Quantity(x, length=y) 3.1 ± 0.01 ns 3.1 ± 0.01 ns 1 ± 0.0046
Quantity/with_numbers/*real 4.02 ± 0.88 ns 3.1 ± 0.01 ns 1.3 ± 0.28
Quantity/with_numbers/^int 8.05 ± 2.2 ns 8.05 ± 2.2 ns 1 ± 0.38
Quantity/with_numbers/^int * real 8.36 ± 2.2 ns 8.36 ± 2.2 ns 1 ± 0.37
Quantity/with_quantity/+y 4.04 ± 0.001 ns 4.04 ± 0.001 ns 1 ± 0.00035
Quantity/with_quantity//y 3.1 ± 0.01 ns 3.1 ± 0.01 ns 1 ± 0.0046
Quantity/with_self/dimension 3.1 ± 0.01 ns 3.1 ± 0.01 ns 1 ± 0.0046
Quantity/with_self/inv 3.11 ± 0.01 ns 3.11 ± 0.01 ns 1 ± 0.0046
Quantity/with_self/ustrip 2.79 ± 0 ns 2.79 ± 0 ns 1 ± 0
QuantityArray/broadcasting/multi_array_of_quantities 0.147 ± 0.0023 ms 0.143 ± 0.0018 ms 1.02 ± 0.021
QuantityArray/broadcasting/multi_normal_array 0.0507 ± 0.003 ms 0.0527 ± 0.003 ms 0.962 ± 0.08
QuantityArray/broadcasting/multi_quantity_array 0.155 ± 0.0015 ms 0.155 ± 0.00086 ms 1 ± 0.011
QuantityArray/broadcasting/x^2_array_of_quantities 24.6 ± 1.9 μs 23.7 ± 2.1 μs 1.04 ± 0.12
QuantityArray/broadcasting/x^2_normal_array 5.57 ± 0.75 μs 5.13 ± 0.85 μs 1.09 ± 0.23
QuantityArray/broadcasting/x^2_quantity_array 6.95 ± 0.26 μs 6.92 ± 0.25 μs 1 ± 0.052
QuantityArray/broadcasting/x^4_array_of_quantities 0.0817 ± 0.00067 ms 0.0814 ± 0.00074 ms 1 ± 0.012
QuantityArray/broadcasting/x^4_normal_array 0.0488 ± 0.0031 ms 0.0484 ± 0.0031 ms 1.01 ± 0.09
QuantityArray/broadcasting/x^4_quantity_array 0.05 ± 0.00021 ms 0.0529 ± 0.003 ms 0.944 ± 0.054
time_to_load 0.208 ± 0.0013 s 0.208 ± 0.0013 s 0.998 ± 0.009
Memory benchmarks
main 4b2d1f3... main / 4b2d1f3...
Quantity/creation/Quantity(x) 0 allocs: 0 B 0 allocs: 0 B
Quantity/creation/Quantity(x, length=y) 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_numbers/*real 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_numbers/^int 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_numbers/^int * real 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_quantity/+y 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_quantity//y 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_self/dimension 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_self/inv 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_self/ustrip 0 allocs: 0 B 0 allocs: 0 B
QuantityArray/broadcasting/multi_array_of_quantities 2 allocs: 0.382 MB 2 allocs: 0.382 MB 1
QuantityArray/broadcasting/multi_normal_array 2 allocs: 0.0763 MB 2 allocs: 0.0763 MB 1
QuantityArray/broadcasting/multi_quantity_array 2 allocs: 0.0763 MB 2 allocs: 0.0763 MB 1
QuantityArray/broadcasting/x^2_array_of_quantities 2 allocs: 0.382 MB 2 allocs: 0.382 MB 1
QuantityArray/broadcasting/x^2_normal_array 2 allocs: 0.0763 MB 2 allocs: 0.0763 MB 1
QuantityArray/broadcasting/x^2_quantity_array 2 allocs: 0.0763 MB 2 allocs: 0.0763 MB 1
QuantityArray/broadcasting/x^4_array_of_quantities 2 allocs: 0.382 MB 2 allocs: 0.382 MB 1
QuantityArray/broadcasting/x^4_normal_array 2 allocs: 0.0763 MB 2 allocs: 0.0763 MB 1
QuantityArray/broadcasting/x^4_quantity_array 2 allocs: 0.0763 MB 2 allocs: 0.0763 MB 1
time_to_load 0.153 k allocs: 14.5 kB 0.153 k allocs: 14.5 kB 1

@github-actions

github-actions Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
main 4b2d1f3... main / 4b2d1f3...
Quantity/creation/Quantity(x) 2.79 ± 0.001 ns 2.79 ± 0.01 ns 1 ± 0.0036
Quantity/creation/Quantity(x, length=y) 3.41 ± 0.01 ns 3.41 ± 0.01 ns 1 ± 0.0042
Quantity/with_numbers/*real 3.1 ± 0.01 ns 3.41 ± 0.01 ns 0.909 ± 0.004
Quantity/with_numbers/^int 12.1 ± 0.99 ns 12.1 ± 0.99 ns 1 ± 0.12
Quantity/with_numbers/^int * real 11.9 ± 0.99 ns 12.2 ± 1.1 ns 0.976 ± 0.12
Quantity/with_quantity/+y 4.04 ± 0.01 ns 4.05 ± 0.07 ns 0.998 ± 0.017
Quantity/with_quantity//y 3.41 ± 0.01 ns 3.41 ± 0.01 ns 1 ± 0.0042
Quantity/with_self/dimension 2.79 ± 0.01 ns 13 ± 0.02 ns 0.216 ± 0.00084
Quantity/with_self/inv 3.11 ± 0.01 ns 3.1 ± 0.01 ns 1 ± 0.0046
Quantity/with_self/ustrip 2.79 ± 0.01 ns 2.79 ± 0.01 ns 1 ± 0.0051
QuantityArray/broadcasting/multi_array_of_quantities 0.109 ± 0.0033 ms 0.109 ± 0.0023 ms 1 ± 0.037
QuantityArray/broadcasting/multi_normal_array 0.0471 ± 0.00025 ms 0.0471 ± 0.00024 ms 1 ± 0.0074
QuantityArray/broadcasting/multi_quantity_array 0.053 ± 0.00021 ms 0.0503 ± 0.0061 ms 1.05 ± 0.13
QuantityArray/broadcasting/x^2_array_of_quantities 22 ± 9.6 μs 22.8 ± 32 μs 0.963 ± 1.4
QuantityArray/broadcasting/x^2_normal_array 2.06 ± 1.6 μs 2.07 ± 5.2 μs 0.995 ± 2.6
QuantityArray/broadcasting/x^2_quantity_array 3.55 ± 0.67 μs 3.55 ± 0.2 μs 1 ± 0.2
QuantityArray/broadcasting/x^4_array_of_quantities 0.0815 ± 0.0043 ms 0.082 ± 0.0059 ms 0.993 ± 0.089
QuantityArray/broadcasting/x^4_normal_array 0.0436 ± 0.00015 ms 0.0436 ± 0.00016 ms 1 ± 0.0051
QuantityArray/broadcasting/x^4_quantity_array 0.0468 ± 0.00017 ms 0.0437 ± 0.00016 ms 1.07 ± 0.0055
time_to_load 0.191 ± 0.01 s 0.185 ± 0.00089 s 1.03 ± 0.057
Memory benchmarks
main 4b2d1f3... main / 4b2d1f3...
Quantity/creation/Quantity(x) 0 allocs: 0 B 0 allocs: 0 B
Quantity/creation/Quantity(x, length=y) 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_numbers/*real 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_numbers/^int 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_numbers/^int * real 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_quantity/+y 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_quantity//y 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_self/dimension 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_self/inv 0 allocs: 0 B 0 allocs: 0 B
Quantity/with_self/ustrip 0 allocs: 0 B 0 allocs: 0 B
QuantityArray/broadcasting/multi_array_of_quantities 3 allocs: 0.382 MB 3 allocs: 0.382 MB 1
QuantityArray/broadcasting/multi_normal_array 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
QuantityArray/broadcasting/multi_quantity_array 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
QuantityArray/broadcasting/x^2_array_of_quantities 3 allocs: 0.382 MB 3 allocs: 0.382 MB 1
QuantityArray/broadcasting/x^2_normal_array 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
QuantityArray/broadcasting/x^2_quantity_array 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
QuantityArray/broadcasting/x^4_array_of_quantities 3 allocs: 0.382 MB 3 allocs: 0.382 MB 1
QuantityArray/broadcasting/x^4_normal_array 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
QuantityArray/broadcasting/x^4_quantity_array 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
time_to_load 0.145 k allocs: 11 kB 0.145 k allocs: 11 kB 1

@MilesCranmer MilesCranmer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MilesCranmerBot please integrate these suggestions

Comment thread src/symbolic_dimensions.jl Outdated
Comment thread src/symbolic_dimensions.jl Outdated
Comment thread src/utils.jl
Comment thread test/unittests.jl
Refine symbolic-dimension hashing to follow the local while-loop style and normalize exponent values structurally, so equal symbolic units hash consistently across representations.

Co-authored-by: Miles Cranmer <miles.cranmer@gmail.com>
Comment thread src/symbolic_dimensions.jl Outdated
Comment thread src/utils.jl
MilesCranmerBot and others added 2 commits April 21, 2026 14:03
@MilesCranmer

Copy link
Copy Markdown
Member

@MilesCranmerBot CI is failing, pls fix

@codecov

codecov Bot commented Apr 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.16%. Comparing base (c2098b5) to head (4b2d1f3).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #213   +/-   ##
=======================================
  Coverage   99.15%   99.16%           
=======================================
  Files          23       23           
  Lines        1305     1320   +15     
=======================================
+ Hits         1294     1309   +15     
  Misses         11       11           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MilesCranmer

MilesCranmer commented Apr 22, 2026

Copy link
Copy Markdown
Member

please ensure that 100% of the diff is covered by testing, it looks like only 83% at the moment.

@MilesCranmer MilesCranmer self-requested a review April 22, 2026 15:03

@MilesCranmer MilesCranmer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MilesCranmerBot see changes. Also please ensure that test coverage of the diff is 100%.

Comment thread src/constants.jl Outdated

macro register_constant(name, value)
return esc(_register_constant(name, value))
return esc(_register_constant(__module__, name, value))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be done as part of this PR. If it is needed, it should be done in a separate PR.

Comment thread src/constants.jl Outdated
Comment on lines +19 to +32
function _register_constant(mod::Module, name::Symbol, value)
s = string(name)
name_symbol = Meta.quot(name)
return quote
const $name = $value
Core.eval($mod, Expr(:const, Expr(:(=), $name_symbol, $value)))
push!(_CONSTANT_SYMBOLS, Symbol($s))
push!(_CONSTANT_VALUES, $name)
push!(_CONSTANT_VALUES, Base.invokelatest(getproperty, $mod, $name_symbol))
end
end

function _register_constant(name::Symbol, value)
return _register_constant(@__MODULE__, name, value)
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above

Comment thread src/register_units.jl Outdated
end

function _register_unit(name::Symbol, value)
return _register_unit(@__MODULE__, name, value)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above

@MilesCranmer

Copy link
Copy Markdown
Member

@MilesCranmerBot CI is failing. Maybe you should use @eval to avoid this issue?

LoadError: syntax: unsupported `const` declaration on local variable around /home/runner/work/DynamicQuantities.jl/DynamicQuantities.jl/src/units.jl:25

Co-authored-by: Miles Cranmer <miles.cranmer@gmail.com>
@MilesCranmer MilesCranmer merged commit 98e91fc into JuliaPhysics:main Apr 22, 2026
7 checks passed
github-actions Bot referenced this pull request Apr 22, 2026
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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