Guide: Parsing solutions - sympy/sympy GitHub Wiki

Parsing Solutions

This guide explains how to programmatically extract information from a solution produced by sympy.solve().[^footnote] For example, you have a system of inequalities of a single variable, x. You want to solve the system of inequalities to determine the bounds of that variable.

[^footnote]: The solutions of sympy.solve() are easier to parse than those of solve_set(). You may be able to convert a solution of solve_set() using solvify() to be similar to the solution of solve().

Specifically, you want to solve the system of inequalities 10 * x >= 5, 10 * x <= 7. The solution will be (1/2 <= x) & (x <= 7/10). So the lower bound of x is 1/2, and the upper bound is 7/10.

Solution: sympy.solve() returns a solution, which you can access in a couple ways depending on what you want to do with it.

(1) If you simply want to view the solution, you can output it as a string, for example (1/2 <= x) & (x <= 7/10).

(2) If you want to process the solution programmatically, you can use its atomic structure to extract information. Atoms are objects, for example x and 1/2. By default, the .atoms() method returns only objects that are truly atomic and cannot be divided into smaller pieces are returned: symbols, numbers, and number symbols like I and pi. It is possible to request atoms of any type, however.

For this situation, you will use SymPy's relational atoms. Relational atoms express the relationship between objects, for example (1/2 <= x) expresses that 1/2 is less than or equal to x.

These relational atoms can be programmatically accessed to extract desired values. Here's how to do that.

Prepare the system of inequalities to be solved

Import SymPy modules

>>> from sympy import parse_expr, solve

Set up inequality strings

>>> inequality_str1 = '10*x >= 5'
>>> inequality_str2 = '10*x <= 7'

Parse the strings into inequalities that SymPy understands:

>>> inequality1 = parse_expr(inequality_str1)
>>> inequality2 = parse_expr(inequality_str2)

Make a system of the inequalities by putting them into a Python list:

>>> inequalities = [inequality1, inequality2]

(1) If you want to view the solution

Call solve()

>>> solve(inequalities)
(1/2 <= x) & (x <= 7/10)

Notice that SymPy has automatically reduced the fraction 5/10 to 1/2.

(2) If you want to programmatically extract information from the solution:

Save the solution to a variable:

>>> solution = solve(inequalities)

If you want to manually verify the solution, call that variable:

>>> solution
(1/2 <= x) & (x <= 7/10)

You can programatically extract information from the solution using .atoms(), which returns the set of all the atoms. Without any arguments, .atoms() returns all indivisible entities (once) in the solution:

>>> solution.atoms()
{x, 7/10, 1/2}

Extracting relational information programmatically using atoms

In this case, you want to extract information about the relations, so you extract the relational atoms by using the Relations argument for .atoms():[^footnote2]

[^footnote2] We recognize that the atoms method is poorly named when used for a relational expression such as 1/2 <= x.

>>> from sympy.core.relational import Relational
>>> relational_atoms = solution.atoms(Relational)
>>> relational_atoms
{1/2 <= x, x <= 7/10}

Now you can extract information about the atoms:

>>> for atom in relational_atoms:
...     print(atom)
1/2 <= x
x <= 7/10

Properties of the relational atom

A Relational atom, for example 1/2 <= x, has several properties of interest:

  1. lhs: the left hand side of the relation, for example 1/2 (value)
  2. rel_op: the relational operator, for example '<=' (string). Other options are '<', '>=', and '>'.
  3. rhs: the right hand side of the relation, for example x (symbol)
  4. lts: the less-than (smaller) side of the relation, for example x in x <= 7/10
  5. gts: the greater-than (larger) side of the relation, for example 7/10 in x <= 7/10

You can extract those properties of each atom:

>>> for atom in relational_atoms:
...     print([atom.lhs, atom.rel_op, atom.rhs])
[1/2, '<=', x]
[x, '<=', 7/10]
>>> for atom in relational_atoms:
...     print([atom.lts, atom.rel_op, atom.gts])
[x, '<=', 7/10]
[1/2, '<=', x]

For this purpose, you want to extract the values (1/2 and 7/10), not the symbol (x). To ensure the symbol is always on the left side, get the canonical form of each relational atom using .canonical:

>>> for atom in relational_atoms:
...     print(atom.canonical)
x >= 1/2
x <= 7/10

Now you can extract the values into a list:

>>> values = []
>>> for atom in relational_atoms:
...     values.append(atom.canonical.rhs)
>>> values
[7/10, 1/2]

To determine the lower and upper bounds, use min and max as you would on any Python list:

>>> lower_bound = min(values)
>>> lower_bound
1/2
>>> upper_bound = max(values)
>>> upper_bound
7/10

More about atoms

You can also extract atoms of given types, for example:

Numbers:

>>> from sympy import Number
>>> solution.atoms(Number)
{7/10, 1/2}

Symbols:

>>> from sympy import Symbol
>>> solution.atoms(Symbol)
{x}

Extracting relational information programmatically using arguments

Solutions can also be decomposed into arguments using args, which returns a tuple of arguments:

>>> solution.args
(1/2 <= x, x <= 7/10)

An argument can also have left-hand side, relational operator, right-hand side, less-than side, and greater-than side properties.

>>> for argument in solution.args:
...     print([atom.lhs, atom.rel_op, atom.rhs])
[1/2, '<=', x]
[x, '<=', 7/10]

Using canonical and rhs (right-hand side) as you did with relational atoms, you can extract the bounds of x:

>>> for argument in solution.args:
>>>	print(argument.canonical.rhs)
1/2
7/10

Similarly, now you can extract the values into a list:

>>> values = []
>>> for atom in relational_atoms:
...     values.append(atom.canonical.rhs)
>>> values
[7/10, 1/2]

To determine the lower and upper bounds, use min and max as you would on any Python list:

>>> lower_bound = min(values)
>>> lower_bound
1/2
>>> upper_bound = max(values)
>>> upper_bound
7/10
⚠️ **GitHub.com Fallback** ⚠️