Found while building a sector input-cost model on top of #292's currency design. Distinct from #318 (which is about Dimension(none) factors being dropped) — here the factor survives and the dimension tag is lost.
The defect
A pseudo-dimension tag does not survive multiplication by a dimensionally-cancelling ratio. The product's dimension silently becomes Dimension(none):
>>> usd = Unit(name="USD", dimension=COUNT)
>>> UnitProduct({UnitFactor(usd, Scale.one): 1.0}).dimension
Dimension(count) # correct
>>> UnitProduct({UnitFactor(usd, Scale.one): 1.0,
... UnitFactor(watt, Scale.kilo): 1.0,
... UnitFactor(watt, Scale.mega): -1.0}).dimension
Dimension(none) # count lost
kW/MW is dimensionless, so count × dimensionless must be count. The tag survives only while the product reduces to a single tagged factor; add a cancelling pair and it demotes. USD·km/m behaves identically, so it is not specific to power.
Why it breaks a real use case
Prices are quoted per one scale and quantities metered in another — universally, in energy. So the product of a quantity and its own rate does not reduce, and then will not aggregate:
>>> Number(0.5, MWh) * Number(48.0, usd_per_MWh)
<24.0 USD> dim = count # matched scales: fine
>>> Number(0.5, kWh) * Number(48.0, usd_per_MWh)
<24.0 kW·USD/MW> dim = none # mismatched: not USD
Both are arithmetically right — canonical_magnitude is 24.0 and 0.024 respectively. But the second is not addable to another USD amount:
>>> steel_cost + power_cost
TypeError: Cannot add Numbers with different dimensions: Dimension(count) vs Dimension(none)
So any cost model that mixes rate denominations fails at aggregation — which is most of them. A sector analysis pulling steel at USD/tonne and power at USD/MWh, against quantities in tonnes and kWh, cannot sum its own line items.
Cause
A UnitProduct's dimension is resolved from the summed dimension vector. A pseudo-dimension contributes the zero vector (ADR 005 — deliberately, so it is multiplicatively transparent), and the tag is not carried through the composition. Zero-vector then resolves to none unless the product happens to reduce to one tagged factor.
Related but separate: the same-dimension ratio is not folded to a scalar at all. kW/MW stays two factors rather than becoming a factor of 1e-3. Magnitude is preserved in canonical_magnitude, so that half is closer to cosmetic — but a fix likely touches the same reduction path, which is why both are noted here.
Scope
Affects every pseudo-dimension, not just count:
| product |
dimension |
expected |
USD |
count |
count |
USD·kW/MW |
none |
count |
USD·km/m |
none |
count |
The same shape applies to radian·m/m, percent·kg/g, and so on.
Relationship to ADR 012
012 asks whether a named dimensionless unit is a distinguishable symbol or unity. This is evidence that the current answer is inconsistent: a tagged unit is distinguishable alone and not distinguishable inside a cancelling product.
Under Option A (dimensionless with kinds) the tag would not exist to lose, and money-ness would ride on the kind — but then the kind's dimension must match the unit's, and a kind cannot refine none while the unit reads count, so the mismatch surfaces there instead.
Under Option B or the count-stays-tagged branch of Option C, this is a straightforward bug: the tag must compose.
Either way it should be fixed or explicitly declared intended, because currency ships on Dimension(count) per 012's recorded decision, and this is the first thing a price calculation hits.
Suggested direction
When a product's summed vector is zero, resolve the dimension from the surviving tagged factors rather than defaulting to none — with a rule for the case where two different tags are present (USD·rad/rad has one tag; USD·rad has two and is arguably ill-formed). Folding same-dimension ratios to a scalar first would make the common case moot and is probably the cheaper half.
Verified against main at d7922fd.
Found while building a sector input-cost model on top of #292's currency design. Distinct from #318 (which is about
Dimension(none)factors being dropped) — here the factor survives and the dimension tag is lost.The defect
A pseudo-dimension tag does not survive multiplication by a dimensionally-cancelling ratio. The product's dimension silently becomes
Dimension(none):kW/MWis dimensionless, socount × dimensionlessmust becount. The tag survives only while the product reduces to a single tagged factor; add a cancelling pair and it demotes.USD·km/mbehaves identically, so it is not specific to power.Why it breaks a real use case
Prices are quoted per one scale and quantities metered in another — universally, in energy. So the product of a quantity and its own rate does not reduce, and then will not aggregate:
Both are arithmetically right —
canonical_magnitudeis24.0and0.024respectively. But the second is not addable to another USD amount:So any cost model that mixes rate denominations fails at aggregation — which is most of them. A sector analysis pulling steel at USD/tonne and power at USD/MWh, against quantities in tonnes and kWh, cannot sum its own line items.
Cause
A
UnitProduct's dimension is resolved from the summed dimension vector. A pseudo-dimension contributes the zero vector (ADR 005 — deliberately, so it is multiplicatively transparent), and the tag is not carried through the composition. Zero-vector then resolves tononeunless the product happens to reduce to one tagged factor.Related but separate: the same-dimension ratio is not folded to a scalar at all.
kW/MWstays two factors rather than becoming a factor of1e-3. Magnitude is preserved incanonical_magnitude, so that half is closer to cosmetic — but a fix likely touches the same reduction path, which is why both are noted here.Scope
Affects every pseudo-dimension, not just
count:USDcountcountUSD·kW/MWnonecountUSD·km/mnonecountThe same shape applies to
radian·m/m,percent·kg/g, and so on.Relationship to ADR 012
012 asks whether a named dimensionless unit is a distinguishable symbol or unity. This is evidence that the current answer is inconsistent: a tagged unit is distinguishable alone and not distinguishable inside a cancelling product.
Under Option A (dimensionless with kinds) the tag would not exist to lose, and money-ness would ride on the kind — but then the kind's dimension must match the unit's, and a kind cannot refine
nonewhile the unit readscount, so the mismatch surfaces there instead.Under Option B or the
count-stays-tagged branch of Option C, this is a straightforward bug: the tag must compose.Either way it should be fixed or explicitly declared intended, because currency ships on
Dimension(count)per 012's recorded decision, and this is the first thing a price calculation hits.Suggested direction
When a product's summed vector is zero, resolve the dimension from the surviving tagged factors rather than defaulting to
none— with a rule for the case where two different tags are present (USD·rad/radhas one tag;USD·radhas two and is arguably ill-formed). Folding same-dimension ratios to a scalar first would make the common case moot and is probably the cheaper half.Verified against
mainatd7922fd.