# how to substitute pint Quantity into equation?

I am exploring ways to check for consistency of units in equations (see also http://ask.sagemath.org/question/1021...) and found that pint (http://pint.readthedocs.org/en/0.6/) seems very convenient, but for some reason it does not work with .subs in sage. Here is an example:

sage: from pint import UnitRegistry
sage: ureg = UnitRegistry()
sage: var('a b c')
sage: b*ureg.meter*c*ureg.meter
<Quantity(1, 'meter ** 2')>
sage: f = (b*c).subs({b: ureg.meter, c: ureg.meter})
Traceback (click to the left of this block for traceback)
...
TypeError: no canonical coercion from <class 'pint.unit.Quantity'>
to Symbolic Ring


Any ideas what could be the reason and if it could be solved easily?

edit retag close merge delete

What are b and c in the Sage session above that you copy paste?

( 2015-03-07 10:54:36 -0500 )edit

Sorry, forgot the var('a b c') line. I just modified my question accordingly.

( 2015-03-09 15:38:40 -0500 )edit

Sort by » oldest newest most voted

pint is not part of Sage. Here you try to substitute some symbolic variables of a symbolic expression by elements of the pint module. This could only work if you could coerce pint units into the Symbolic Ring. The way to do this is to define a coercion between pint units and the Symbolic Ring, you can have a look at the following docs:

This should be easily doable by using the Sage units but i guess it is not what you would like to do since you will lose the consistency checking of pint, for example with Sage units, you can do:

sage: units.length.meter + units.time.second
meter + second


So perhaps a substitution i not what you would like to do. Perhaps, you should instead create a .physical_consistency() or .pint() (or whatever suitable name) method for symbolic elements, that recurses to the tree of the symbolic expression and make the units replacement from your dictionary.

Note that for powers you will experience some problems because the .__pow__() method is not able to deal with Sage integers:

sage: sage: ureg.meter^2
TypeError: Cannot power UnitsContainer by <type 'sage.rings.integer.Integer'>


EDIT Here is a prototype. The idea is to recursively unfold the symbolic expression and refold it with units:

def physical(expr, dico):
r"""
INPUT:

- expr is a symbolic expression.
- dico is a dictionnary that maps symbols to units (possibly with magnitude)

The units are from the pint package, please do sage -pip install pint to let this function work.

OUTPUT:

The resulting unit (with magnitude) or raise an error.
"""
op = expr.operator()
dimensionless = (dico.values()[0] / dico.values()[0])
if op:
return sum([is_physical(i, dico) for i in expr.operands()])
elif op == operator.mul:
return prod([is_physical(i, dico) for i in expr.operands()])
elif op == operator.pow: # todo : improve for dimensionless powers, like for log.
return is_physical(expr.operands()[0], dico) ** float(expr.operands()[1])
elif op in [exp, log, cos, sin]: # to enlarge
if is_physical(expr.operands()[0], dico).dimensionality == dimensionless.dimensionality:
return op(is_physical(expr.operands()[0], dico).magnitude) * dimensionless
else:
raise ValueError('The {} can only eat dimensionless numbers.'.format(op))
else:
raise ValueError('I do not know how to deal with {} operator (yet).'.format(op))
else:
if expr.is_symbol():
if expr in dico:
return dico[expr]
else:
raise ValueError('The symbol {} did not get any unit.'.format(expr))
elif expr.is_numeric():
return float(expr) * dimensionless
elif expr in [e, pi]:
return float(expr) * dimensionless
else:
raise ValueError('I do not know how to deal with {} operand (yet).'.format(expr))


The reason why i did not use expr.is__constant(), but explicitely list e and pi is because things like NaN are constants, and i am not sure there will not be physical constants (that are not dimensionless). If complex numbers might be involved, it could be better to replace ...

more

Thanks for the quick answer, Thierry! There ain't no easy way, it seems. Given that pint has to be installed into sage manually, maybe the effort will be better spent on doing it with the included Sage units, as you outlined in your answer to http://ask.sagemath.org/question/1021.... Or I keep going with my old hacky way for the moment, where I have been doing what you proposed above, substituting variables with units from a dictionary, but the trick was actually to substitute with the original variable multiplied by the unit to avoid that subtractions cancel out units. Need to then divide by the oringial equation and run full_simplify() to separate the units from the variables, which does not always work and is not elegant...

( 2015-03-08 17:30:06 -0500 )edit

It's a curse! I tried out pint, I tried out dimpy, which is another awesome units package, and I tried out creating my own class and newly defining addition to use with the inbuilt units package. But in all cases, the .subs() command does not work! So either I use the sage units, which seem to behave just like regular expressions, and lose the ability to perform automatic units checks because of the cancelling of units by subtraction, or I use a more sensible system, but then cannot substitute units for variables, so again cannot perform automatic units checks. If anyone could put up an example how to coerce between some units package and the Symbolic Ring, that would be really helpful.

( 2015-03-09 16:24:53 -0500 )edit

As i explained before, even with other packages, there is no hope that the .subs() method will work, unless someone define a coercion between the Symbolic Ring and the elements coming from the package. I provided a prototype of what could be a lighter solution.

( 2015-03-09 18:00:06 -0500 )edit

Wow, thanks, Thierry. Couldn't this be done in a similar way using Sage units? Just walk the tree to the last branch and then substitute the units for the variables at each step, defining different rules for different operators, e.g. check if all terms are the same for additions and subtractions and return the common units, check that all units cancel out for sin, cos, log and exponents etc. Looks like what you already did, but with pint units, right?

( 2015-03-09 18:12:34 -0500 )edit

Yes, but the problem is that with Sage units nothing will prevent cancellations during sums. This might be doable if at the end we only want the unit, not the magnitude (because 1*meter - 1*meter will result in 0 that is dimensionless). So, if you want to keep the result as both (magnitude,unit), it seems that pint or similar software will behave better.

( 2015-03-09 18:28:30 -0500 )edit