35 views

Skip to first unread message

May 27, 2020, 4:37:33 PM5/27/20

to sympy

Hello,

I have a string written in Latex for which I know the dimensions of each symbol. My goal is to validate the dimensional consistency of the expression. I'm having trouble with substitution. For example,

>>> from sympy.physics.units import mass, length, time

>>> from sympy.physics.units.systems.si import dimsys_SI

>>> from sympy.parsing.latex import parse_latex

>>> eq = parse_latex("F = m a")

>>> eq

Eq(F, a*m)

I can get the symbols from that expression

>>> set_of_symbols_in_eq = eq.free_symbols

And for each symbol in the set I know what dimensions each has:

>>> Fdim = mass * length / time**2

>>> mdim = mass

>>> adim = length / time**2

When I try substituting the dimensions into the original expression, I get an error

>>> eq.subs({F: Fdim, m: mdim, a: adim})

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

NameError: name 'F' is not defined

That is surprising, because *F* is a Symbol:

>>> eq.lhs

F

>>> type(eq.lhs)

<class 'sympy.core.symbol.Symbol'>

I think that error means that although *F* is a Symbol, there isn't a variable named F that points to the Symbol F?

If that's the case, I don't know how to access the symbols in the abstract syntax tree provided by *eq*.

How would I indicate to SymPy that "F = m a" in *eq* has variables with certain dimensions?

My goal is to run

>>> dimsys_SI.equivalent_dims(Fdim, mdim * adim)

True

without retyping the expression.

I think I want something like the following, except with dimensions substituted for each symbol.

>>> dimsys_SI.equivalent_dims( eq.lhs, eq.rhs )

False

May 27, 2020, 7:01:02 PM5/27/20

to sympy

You're right that you have to define the Python variable name to

access F like that. See

https://docs.sympy.org/latest/tutorial/gotchas.html.

You can get all the symbols in an expression with eq.free_symbols. Or

if you know the symbol is F you can just set

F = symbols('F')

since symbols with the same name are equal, so F will be the same as

the symbol F in the expression from parse_latex.

Aaron Meurer

> --

> You received this message because you are subscribed to the Google Groups "sympy" group.

> To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.

> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/a4e2b3fe-27b2-45b8-a7f6-598caea772de%40googlegroups.com.

access F like that. See

https://docs.sympy.org/latest/tutorial/gotchas.html.

You can get all the symbols in an expression with eq.free_symbols. Or

if you know the symbol is F you can just set

F = symbols('F')

since symbols with the same name are equal, so F will be the same as

the symbol F in the expression from parse_latex.

Aaron Meurer

> You received this message because you are subscribed to the Google Groups "sympy" group.

> To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.

> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/a4e2b3fe-27b2-45b8-a7f6-598caea772de%40googlegroups.com.

May 27, 2020, 8:35:32 PM5/27/20

to sympy

Thanks Aaron for your help.

With your guidance, I solved my problem (though my use of eval() feels hacky).

>>> import sympy

>>> from sympy.physics.units import mass, length, time

>>> from sympy.physics.units.systems.si import dimsys_SI

>>> from sympy.parsing.latex import parse_latex

>>> eq = parse_latex("F = m a")

>>> F = mass * length / time**2

>>> m = mass

>>> a = length / time**2

>>> dimsys_SI.equivalent_dims( eval(str(eq.lhs)), eval(str(eq.rhs)) )

True

I used eval() rather than variable substitution

>>> Fdim = mass * length / time**2

>>> mdim = mass

>>> adim = length / time**2

>>> lhs_dim = eq.lhs.subs([(F, Fdim), (m, mdim), (a, adim)])

>>> rhs_dim = eq.rhs.subs([(F, Fdim), (m, mdim), (a, adim)])

because I was not able to figure out how to simplify the RHS

>>> rhs_dim

Dimension(length/time**2)*Dimension(mass, M)

Even though the RHS dimensions simplify to be equivalent to the LHS, I get an error when I compare the LHS and RHS:

>>> dimsys_SI.equivalent_dims( lhs_dim, rhs_dim )

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

File "/usr/local/lib/python3.6/dist-packages/sympy/physics/units/dimensions.py", line 455, in equivalent_dims

deps2 = self.get_dimensional_dependencies(dim2)

File "/usr/local/lib/python3.6/dist-packages/sympy/physics/units/dimensions.py", line 448, in get_dimensional_dependencies

dimdep = self._get_dimensional_dependencies_for_name(name)

File "/usr/local/lib/python3.6/dist-packages/sympy/physics/units/dimensions.py", line 422, in _get_dimensional_dependencies_for_name

for k, v in d.items():

AttributeError: 'NoneType' object has no attribute 'items'

On Wednesday, May 27, 2020 at 7:01:02 PM UTC-4, Aaron Meurer wrote:

You're right that you have to define the Python variable name to

access F like that. See

https://docs.sympy.org/latest/tutorial/gotchas.html.

You can get all the symbols in an expression with eq.free_symbols. Or

if you know the symbol is F you can just set

F = symbols('F')

since symbols with the same name are equal, so F will be the same as

the symbol F in the expression from parse_latex.

Aaron Meurer

> To unsubscribe from this group and stop receiving emails from it, send an email to sy...@googlegroups.com.

May 27, 2020, 8:59:42 PM5/27/20

to sympy

Why are you using eval(str(eq.lhs))? That should just give back eq.lhs.

Aaron Meurer

> To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.

> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/6e6c4358-7894-4099-9d27-74c5fefe2a31%40googlegroups.com.

Aaron Meurer

> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/6e6c4358-7894-4099-9d27-74c5fefe2a31%40googlegroups.com.

May 27, 2020, 9:48:02 PM5/27/20

to sympy

I agree with your assessment.

Here's my minimum working example where I don't use eval(str()) and get the problem.

>>> import sys

>>> sys.version

'3.6.9 (default, Apr 18 2020, 01:56:04) \n[GCC 8.4.0]'

>>> import sympy

>>> sympy.__version__

'1.5.1'

>>> from sympy.physics.units import mass, length, time

>>> from sympy.physics.units.systems.si import dimsys_SI

>>> from sympy.parsing.latex import parse_latex

>>> eq = parse_latex("F = m a")

>>> F = mass * length / time**2

>>> m = mass

>>> a = length / time**2

>>> dimsys_SI.equivalent_dims( eq.lhs, eq.rhs )

False

For context, here are the variables

>>> eq.lhs

F

>>> eq.rhs

a*m

>>> F

Dimension(length*mass/time**2)

>>> a

Dimension(length/time**2)

>>> a*m

Dimension(length*mass/time**2)

>>> m

Dimension(mass, M)

On Wednesday, May 27, 2020 at 8:59:42 PM UTC-4, Aaron Meurer wrote:

Why are you using eval(str(eq.lhs))? That should just give back eq.lhs.

Aaron Meurer

> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/6e6c4358-7894-4099-9d27-74c5fefe2a31%40googlegroups.com.

May 28, 2020, 8:08:25 AM5/28/20

to sympy

Good morning,

To verify that the issue is not due to using an old version, I re-ran the same example with the current development version of SymPy.

>>> import sys

>>> sys.version

'3.6.9 (default, Apr 18 2020, 01:56:04) \n[GCC 8.4.0]'

>>> sympy.__version__

'1.7.dev'

>>> from sympy.physics.units import mass, length, time

>>> from sympy.physics.units.systems.si import dimsys_SI

>>> from sympy.parsing.latex import parse_latex

>>> eq = parse_latex("F = m a")

>>> F = mass * length / time**2

>>> m = mass

>>> a = length / time**2

>>> dimsys_SI.equivalent_dims( eq.lhs, eq.rhs )

False

>>> dimsys_SI.equivalent_dims( eval(str(eq.lhs)), eval(str(eq.rhs)) )

True

Jun 2, 2020, 11:46:41 AM6/2/20

to sympy

The issue here is you're confusing Python variables and SymPy symbols.

The use of eval() isn't helping, because eval() evaluates based on the

Python variables (I would try to avoid eval() if possible). See

https://docs.sympy.org/latest/tutorial/gotchas.html. Once you define

F, m, and a, they have no bearing on the symbols that are present in

eq. SymPy has no way to know that there are Python variables named F,

m, and a when looking at eq.

What you can do is

F, m, a = symbols('F m a')

eq.subs({F: mass*length/time**2, m: mass, a: length/time**2}).

to replace the symbols with units in the equation.

Aaron Meurer

> To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.

> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/35ed29bb-4bdf-4229-92a0-26b3b1449092%40googlegroups.com.

The use of eval() isn't helping, because eval() evaluates based on the

Python variables (I would try to avoid eval() if possible). See

https://docs.sympy.org/latest/tutorial/gotchas.html. Once you define

F, m, and a, they have no bearing on the symbols that are present in

eq. SymPy has no way to know that there are Python variables named F,

m, and a when looking at eq.

What you can do is

F, m, a = symbols('F m a')

eq.subs({F: mass*length/time**2, m: mass, a: length/time**2}).

to replace the symbols with units in the equation.

Aaron Meurer

> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/35ed29bb-4bdf-4229-92a0-26b3b1449092%40googlegroups.com.

Jun 2, 2020, 3:01:19 PM6/2/20

to sympy

Hello,

I appreciate your patience in explaining my issue. Checking dimensions in Sympy is useful for my work, so I am seeking to implement the check correctly.

I understand what you're saying about Python variables versus Sympy Symbols. There are two examples in my response below. The first ("Example 1") is an attempt to use the substitution-based approach. In Example 2 I realized I could reduce my issue to three lines (which exclude irrelevant aspects of Latex and Python variables).

Example 1 using latex to Sympy to dimensions

Here's what I get when I use substitution of dimensions:

>>> import sys

>>> sys.version

'3.6.9 (default, Apr 18 2020, 01:56:04) \n[GCC 8.4.0]'

>>> import sympy

>>> sympy.__version__

'1.7.dev'

>>> from sympy.physics.units import mass, length, time

>>> from sympy.physics.units.systems.si import dimsys_SI

>>> from sympy.parsing.latex import parse_latex

Define the expression in Latex, then convert to Sympy expression

>>> eq = parse_latex("F = m a")

>>> F, m, a = sympy.symbols('F m a')

These three on the left side are Python variables

For each Sympy symbol in eq, replace with the Sympy dimensions

>>> eq.subs({F: mass*length/time**2, m: mass, a: length/time**2})

False

That boolean return wasn't what I was aiming for, so below I break the process down

Separate substitution into two steps and review the output

>>> eq.lhs.subs({F: mass*length/time**2, m: mass, a: length/time**2})

Dimension(length*mass/time**2)

>>> eq.rhs.subs({F: mass*length/time**2, m: mass, a: length/time**2})

Dimension(length/time**2)*Dimension(mass, M)

Now that I have confidence the Sympy substitution is behaving as expected, save the results to two Python variables

>>> RHS = eq.rhs.subs({F: mass*length/time**2, m: mass, a: length/time**2})

>>> RHS

Dimension(length/time**2)*Dimension(mass, M)

>>> type(RHS)

<class 'sympy.core.mul.Mul'>

That Mul is potentially the cause of my issue with dimsys_SI.equivalent_dims below. If there is a way to simplify the Mul of Dimension class objects, that would probably eliminate the issue.

The left-hand side of eq returns a Dimension class object

>>> LHS = eq.lhs.subs({F: mass*length/time**2, m: mass, a: length/time**2})

>>> LHS

Dimension(length*mass/time**2)

>>> type(LHS)

<class 'sympy.physics.units.dimensions.Dimension'>

The issue of failing to compare the left-side dimensions and the right-side dimensions could be due to the Mul, but I do not know how to simplify RHS. Or I may be incorrectly supplying Python variables LHS and RHS

>>> dimsys_SI.equivalent_dims( RHS, LHS )

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

File "/usr/local/lib/python3.6/dist-packages/sympy/physics/units/dimensions.py", line 454, in equivalent_dims

deps1 = self.get_dimensional_dependencies(dim1)

File "/usr/local/lib/python3.6/dist-packages/sympy/physics/units/dimensions.py", line 448, in get_dimensional_dependencies

dimdep = self._get_dimensional_dependencies_for_name(name)

File "/usr/local/lib/python3.6/dist-packages/sympy/physics/units/dimensions.py", line 422, in _get_dimensional_dependencies_for_name

for k, v in d.items():

AttributeError: 'NoneType' object has no attribute 'items'

After writing my example 1, I realized there is a simpler example of the issue.

Example 2: the short version

>>> import sys

>>> sys.version

'3.6.9 (default, Apr 18 2020, 01:56:04) \n[GCC 8.4.0]'

>>> import sympy

>>> sympy.__version__

'1.7.dev'

>>> from sympy.physics.units import mass, length

>>> from sympy.physics.units.systems.si import dimsys_SI

>>> sympy.Mul(mass, length)

Dimension(length, L)*Dimension(mass, M)

>>> mass*length

Dimension(length*mass)

The above two lines appear equivalent but not equal

Same issue as in Example 1,

>>> dimsys_SI.equivalent_dims( sympy.Mul(mass, length), mass*length )

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

File "/opt/sympy/physics/units/dimensions.py", line 461, in equivalent_dims

deps1 = self.get_dimensional_dependencies(dim1)

File "/opt/sympy/physics/units/dimensions.py", line 455, in get_dimensional_dependencies

dimdep = self._get_dimensional_dependencies_for_name(name)

File "/opt/sympy/physics/units/dimensions.py", line 429, in _get_dimensional_dependencies_for_name

for k, v in d.items():

AttributeError: 'NoneType' object has no attribute 'items'

I think the reason is the Mul. However, I do not know how to simplify to Dimension

>>> type(mass*length)

<class 'sympy.physics.units.dimensions.Dimension'>

>>> type(sympy.Mul(mass, length))

<class 'sympy.core.mul.Mul'>

>>> sympy.simplify(sympy.Mul(mass, length))

Dimension(length, L)*Dimension(mass, M)

Ben

Reply all

Reply to author

Forward

0 new messages

Search

Clear search

Close search

Google apps

Main menu