algebra_with_sympy.algebraic_equation

This package uses a special version of sympy which defines an equation with a left-hand-side (lhs) and a right- hand-side (rhs) connected by the "=" operator (e.g. p*V = n*R*T).

The intent is to allow using the mathematical tools in SymPy to rearrange equations and perform algebra in a stepwise fashion. In this way more people can successfully perform algebraic rearrangements without stumbling over missed details such as a negative sign. This mimics the capabilities available in SageMath and Maxima.

This package also provides convenient settings for interactive use on the command line, in ipython and Jupyter notebook environments. See the documentation at https://gutow.github.io/Algebra_with_Sympy/.

Explanation

This class defines relations that all high school and college students would recognize as mathematical equations. At present only the "=" relation operator is recognized.

This class is intended to allow using the mathematical tools in SymPy to rearrange equations and perform algebra in a stepwise fashion. In this way more people can successfully perform algebraic rearrangements without stumbling over missed details such as a negative sign.

Create an equation with the call Equation(lhs,rhs), where lhs and rhs are any valid Sympy expression. Eqn(...) is a synonym for Equation(...).

Parameters

lhs: sympy expression, class Expr. rhs: sympy expression, class Expr. kwargs:

Examples

NOTE: All the examples below are in vanilla python. You can get human readable eqautions "lhs = rhs" in vanilla python by adjusting the settings in algwsym_config (see it's documentation). Output is human readable by default in IPython and Jupyter environments.

>>> from algebra_with_sympy import *
>>> a, b, c, x = var('a b c x')
>>> Equation(a,b/c)
Equation(a, b/c)
>>> t=Eqn(a,b/c)
>>> t
Equation(a, b/c)
>>> t*c
Equation(a*c, b)
>>> c*t
Equation(a*c, b)
>>> exp(t)
Equation(exp(a), exp(b/c))
>>> exp(log(t))
Equation(a, b/c)

Simplification and Expansion

>>> f = Eqn(x**2 - 1, c)
>>> f
Equation(x**2 - 1, c)
>>> f/(x+1)
Equation((x**2 - 1)/(x + 1), c/(x + 1))
>>> (f/(x+1)).simplify()
Equation(x - 1, c/(x + 1))
>>> simplify(f/(x+1))
Equation(x - 1, c/(x + 1))
>>> (f/(x+1)).expand()
Equation(x**2/(x + 1) - 1/(x + 1), c/(x + 1))
>>> expand(f/(x+1))
Equation(x**2/(x + 1) - 1/(x + 1), c/(x + 1))
>>> factor(f)
Equation((x - 1)*(x + 1), c)
>>> f.factor()
Equation((x - 1)*(x + 1), c)
>>> f2 = f+a*x**2+b*x +c
>>> f2
Equation(a*x**2 + b*x + c + x**2 - 1, a*x**2 + b*x + 2*c)
>>> collect(f2,x)
Equation(b*x + c + x**2*(a + 1) - 1, a*x**2 + b*x + 2*c)

Apply operation to only one side

>>> poly = Eqn(a*x**2 + b*x + c*x**2, a*x**3 + b*x**3 + c*x)
>>> poly.applyrhs(factor,x)
Equation(a*x**2 + b*x + c*x**2, x*(c + x**2*(a + b)))
>>> poly.applylhs(factor)
Equation(x*(a*x + b + c*x), a*x**3 + b*x**3 + c*x)
>>> poly.applylhs(collect,x)
Equation(b*x + x**2*(a + c), a*x**3 + b*x**3 + c*x)

.apply... also works with user defined python functions

>>> def addsquare(eqn):
...     return eqn+eqn**2
...
>>> t.apply(addsquare)
Equation(a**2 + a, b**2/c**2 + b/c)
>>> t.applyrhs(addsquare)
Equation(a, b**2/c**2 + b/c)
>>> t.apply(addsquare, side = 'rhs')
Equation(a, b**2/c**2 + b/c)
>>> t.applylhs(addsquare)
Equation(a**2 + a, b/c)
>>> addsquare(t)
Equation(a**2 + a, b**2/c**2 + b/c)

Inaddition to .apply... there is also the less general .do, .dolhs, .dorhs, which only works for operations defined on the Expr class (e.g..collect(), .factor(), .expand(), etc...).

>>> poly.dolhs.collect(x)
Equation(b*x + x**2*(a + c), a*x**3 + b*x**3 + c*x)
>>> poly.dorhs.collect(x)
Equation(a*x**2 + b*x + c*x**2, c*x + x**3*(a + b))
>>> poly.do.collect(x)
Equation(b*x + x**2*(a + c), c*x + x**3*(a + b))
>>> poly.dorhs.factor()
Equation(a*x**2 + b*x + c*x**2, x*(a*x**2 + b*x**2 + c))

poly.do.exp() or other sympy math functions will raise an error.

Rearranging an equation (simple example made complicated as illustration)

>>> p, V, n, R, T = var('p V n R T')
>>> eq1=Eqn(p*V,n*R*T)
>>> eq1
Equation(V*p, R*T*n)
>>> eq2 =eq1/V
>>> eq2
Equation(p, R*T*n/V)
>>> eq3 = eq2/R/T
>>> eq3
Equation(p/(R*T), n/V)
>>> eq4 = eq3*R/p
>>> eq4
Equation(1/T, R*n/(V*p))
>>> 1/eq4
Equation(T, V*p/(R*n))
>>> eq5 = 1/eq4 - T
>>> eq5
Equation(0, -T + V*p/(R*n))

Substitution (#'s and units)

>>> L, atm, mol, K = var('L atm mol K', positive=True, real=True) # units
>>> eq2.subs({R:0.08206*L*atm/mol/K,T:273*K,n:1.00*mol,V:24.0*L})
Equation(p, 0.9334325*atm)
>>> eq2.subs({R:0.08206*L*atm/mol/K,T:273*K,n:1.00*mol,V:24.0*L}).evalf(4)
Equation(p, 0.9334*atm)

Substituting an equation into another equation:

>>> P, P1, P2, A1, A2, E1, E2 = symbols("P, P1, P2, A1, A2, E1, E2")
>>> eq1 = Eqn(P, P1 + P2)
>>> eq2 = Eqn(P1 / (A1 * E1), P2 / (A2 * E2))
>>> P1_val = (eq1 - P2).swap
>>> P1_val
Equation(P1, P - P2)
>>> eq2 = eq2.subs(P1_val)
>>> eq2
Equation((P - P2)/(A1*E1), P2/(A2*E2))
>>> P2_val = solve(eq2.subs(P1_val), P2).args[0]
>>> P2_val
Equation(P2, A2*E2*P/(A1*E1 + A2*E2))

Combining equations (Math with equations: lhs with lhs and rhs with rhs)

>>> q = Eqn(a*c, b/c**2)
>>> q
Equation(a*c, b/c**2)
>>> t
Equation(a, b/c)
>>> q+t
Equation(a*c + a, b/c + b/c**2)
>>> q/t
Equation(c, 1/c)
>>> t**q
Equation(a**(a*c), (b/c)**(b/c**2))

Utility operations

>>> t.reversed
Equation(b/c, a)
>>> t.swap
Equation(b/c, a)
>>> t.lhs
a
>>> t.rhs
b/c
>>> t.as_Boolean()
Eq(a, b/c)

.check() convenience method for .as_Boolean().simplify()

>>> from sympy import I, pi
>>> Equation(pi*(I+2), pi*I+2*pi).check()
True
>>> Eqn(a,a+1).check()
False

Differentiation Differentiation is applied to both sides if the wrt variable appears on both sides.

>>> q=Eqn(a*c, b/c**2)
>>> q
Equation(a*c, b/c**2)
>>> diff(q,b)
Equation(Derivative(a*c, b), c**(-2))
>>> diff(q,c)
Equation(a, -2*b/c**3)
>>> diff(log(q),b)
Equation(Derivative(log(a*c), b), 1/b)
>>> diff(q,c,2)
Equation(Derivative(a, c), 6*b/c**4)

If you specify multiple differentiation all at once the assumption is order of differentiation matters and the lhs will not be evaluated.

>>> diff(q,c,b)
Equation(Derivative(a*c, b, c), -2/c**3)

To overcome this specify the order of operations.

>>> diff(diff(q,c),b)
Equation(Derivative(a, b), -2/c**3)

But the reverse order returns an unevaulated lhs (a may depend on b).

>>> diff(diff(q,b),c)
Equation(Derivative(a*c, b, c), -2/c**3)

Integration can only be performed on one side at a time.

>>> q=Eqn(a*c,b/c)
>>> integrate(q,b,side='rhs')
b**2/(2*c)
>>> integrate(q,b,side='lhs')
a*b*c

Make a pretty statement of integration from an equation

>>> Eqn(Integral(q.lhs,b),integrate(q,b,side='rhs'))
Equation(Integral(a*c, b), b**2/(2*c))

Integration of each side with respect to different variables

>>> q.dorhs.integrate(b).dolhs.integrate(a)
Equation(a**2*c/2, b**2/(2*c))

Automatic solutions using sympy solvers. THIS IS EXPERIMENTAL. Please report issues at https://github.com/gutow/Algebra_with_Sympy/issues.

>>> tosolv = Eqn(a - b, c/a)
>>> solve(tosolv,a)
FiniteSet(Equation(a, b/2 - sqrt(b**2 + 4*c)/2), Equation(a, b/2 + sqrt(b**2 + 4*c)/2))
>>> solve(tosolv, b)
FiniteSet(Equation(b, (a**2 - c)/a))
>>> solve(tosolv, c)
FiniteSet(Equation(c, a**2 - a*b))
  1"""
  2This package uses a special version of sympy which defines an equation 
  3with a left-hand-side (lhs) and a right-
  4hand-side (rhs) connected by the "=" operator (e.g. `p*V = n*R*T`).
  5
  6The intent is to allow using the mathematical tools in SymPy to rearrange
  7equations and perform algebra in a stepwise fashion. In this way more people
  8can successfully perform algebraic rearrangements without stumbling over
  9missed details such as a negative sign. This mimics the capabilities available
 10in [SageMath](https://www.sagemath.org/) and
 11[Maxima](http://maxima.sourceforge.net/).
 12
 13This package also provides convenient settings for interactive use on the 
 14command line, in ipython and Jupyter notebook environments. See the 
 15documentation at https://gutow.github.io/Algebra_with_Sympy/.
 16
 17Explanation
 18===========
 19This class defines relations that all high school and college students
 20would recognize as mathematical equations. At present only the "=" relation
 21operator is recognized.
 22
 23This class is intended to allow using the mathematical tools in SymPy to
 24rearrange equations and perform algebra in a stepwise fashion. In this
 25way more people can successfully perform algebraic rearrangements without
 26stumbling over missed details such as a negative sign.
 27
 28Create an equation with the call ``Equation(lhs,rhs)``, where ``lhs`` and
 29``rhs`` are any valid Sympy expression. ``Eqn(...)`` is a synonym for
 30``Equation(...)``.
 31
 32Parameters
 33==========
 34lhs: sympy expression, ``class Expr``.
 35rhs: sympy expression, ``class Expr``.
 36kwargs:
 37
 38Examples
 39========
 40NOTE: All the examples below are in vanilla python. You can get human
 41readable eqautions "lhs = rhs" in vanilla python by adjusting the settings
 42in `algwsym_config` (see it's documentation). Output is human readable by
 43default in IPython and Jupyter environments.
 44>>> from algebra_with_sympy import *
 45>>> a, b, c, x = var('a b c x')
 46>>> Equation(a,b/c)
 47Equation(a, b/c)
 48>>> t=Eqn(a,b/c)
 49>>> t
 50Equation(a, b/c)
 51>>> t*c
 52Equation(a*c, b)
 53>>> c*t
 54Equation(a*c, b)
 55>>> exp(t)
 56Equation(exp(a), exp(b/c))
 57>>> exp(log(t))
 58Equation(a, b/c)
 59
 60Simplification and Expansion
 61>>> f = Eqn(x**2 - 1, c)
 62>>> f
 63Equation(x**2 - 1, c)
 64>>> f/(x+1)
 65Equation((x**2 - 1)/(x + 1), c/(x + 1))
 66>>> (f/(x+1)).simplify()
 67Equation(x - 1, c/(x + 1))
 68>>> simplify(f/(x+1))
 69Equation(x - 1, c/(x + 1))
 70>>> (f/(x+1)).expand()
 71Equation(x**2/(x + 1) - 1/(x + 1), c/(x + 1))
 72>>> expand(f/(x+1))
 73Equation(x**2/(x + 1) - 1/(x + 1), c/(x + 1))
 74>>> factor(f)
 75Equation((x - 1)*(x + 1), c)
 76>>> f.factor()
 77Equation((x - 1)*(x + 1), c)
 78>>> f2 = f+a*x**2+b*x +c
 79>>> f2
 80Equation(a*x**2 + b*x + c + x**2 - 1, a*x**2 + b*x + 2*c)
 81>>> collect(f2,x)
 82Equation(b*x + c + x**2*(a + 1) - 1, a*x**2 + b*x + 2*c)
 83
 84Apply operation to only one side
 85>>> poly = Eqn(a*x**2 + b*x + c*x**2, a*x**3 + b*x**3 + c*x)
 86>>> poly.applyrhs(factor,x)
 87Equation(a*x**2 + b*x + c*x**2, x*(c + x**2*(a + b)))
 88>>> poly.applylhs(factor)
 89Equation(x*(a*x + b + c*x), a*x**3 + b*x**3 + c*x)
 90>>> poly.applylhs(collect,x)
 91Equation(b*x + x**2*(a + c), a*x**3 + b*x**3 + c*x)
 92
 93``.apply...`` also works with user defined python functions
 94>>> def addsquare(eqn):
 95...     return eqn+eqn**2
 96...
 97>>> t.apply(addsquare)
 98Equation(a**2 + a, b**2/c**2 + b/c)
 99>>> t.applyrhs(addsquare)
100Equation(a, b**2/c**2 + b/c)
101>>> t.apply(addsquare, side = 'rhs')
102Equation(a, b**2/c**2 + b/c)
103>>> t.applylhs(addsquare)
104Equation(a**2 + a, b/c)
105>>> addsquare(t)
106Equation(a**2 + a, b**2/c**2 + b/c)
107
108Inaddition to ``.apply...`` there is also the less general ``.do``,
109``.dolhs``, ``.dorhs``, which only works for operations defined on the
110``Expr`` class (e.g.``.collect(), .factor(), .expand()``, etc...).
111>>> poly.dolhs.collect(x)
112Equation(b*x + x**2*(a + c), a*x**3 + b*x**3 + c*x)
113>>> poly.dorhs.collect(x)
114Equation(a*x**2 + b*x + c*x**2, c*x + x**3*(a + b))
115>>> poly.do.collect(x)
116Equation(b*x + x**2*(a + c), c*x + x**3*(a + b))
117>>> poly.dorhs.factor()
118Equation(a*x**2 + b*x + c*x**2, x*(a*x**2 + b*x**2 + c))
119
120``poly.do.exp()`` or other sympy math functions will raise an error.
121
122Rearranging an equation (simple example made complicated as illustration)
123>>> p, V, n, R, T = var('p V n R T')
124>>> eq1=Eqn(p*V,n*R*T)
125>>> eq1
126Equation(V*p, R*T*n)
127>>> eq2 =eq1/V
128>>> eq2
129Equation(p, R*T*n/V)
130>>> eq3 = eq2/R/T
131>>> eq3
132Equation(p/(R*T), n/V)
133>>> eq4 = eq3*R/p
134>>> eq4
135Equation(1/T, R*n/(V*p))
136>>> 1/eq4
137Equation(T, V*p/(R*n))
138>>> eq5 = 1/eq4 - T
139>>> eq5
140Equation(0, -T + V*p/(R*n))
141
142Substitution (#'s and units)
143>>> L, atm, mol, K = var('L atm mol K', positive=True, real=True) # units
144>>> eq2.subs({R:0.08206*L*atm/mol/K,T:273*K,n:1.00*mol,V:24.0*L})
145Equation(p, 0.9334325*atm)
146>>> eq2.subs({R:0.08206*L*atm/mol/K,T:273*K,n:1.00*mol,V:24.0*L}).evalf(4)
147Equation(p, 0.9334*atm)
148
149Substituting an equation into another equation:
150>>> P, P1, P2, A1, A2, E1, E2 = symbols("P, P1, P2, A1, A2, E1, E2")
151>>> eq1 = Eqn(P, P1 + P2)
152>>> eq2 = Eqn(P1 / (A1 * E1), P2 / (A2 * E2))
153>>> P1_val = (eq1 - P2).swap
154>>> P1_val
155Equation(P1, P - P2)
156>>> eq2 = eq2.subs(P1_val)
157>>> eq2
158Equation((P - P2)/(A1*E1), P2/(A2*E2))
159>>> P2_val = solve(eq2.subs(P1_val), P2).args[0]
160>>> P2_val
161Equation(P2, A2*E2*P/(A1*E1 + A2*E2))
162
163Combining equations (Math with equations: lhs with lhs and rhs with rhs)
164>>> q = Eqn(a*c, b/c**2)
165>>> q
166Equation(a*c, b/c**2)
167>>> t
168Equation(a, b/c)
169>>> q+t
170Equation(a*c + a, b/c + b/c**2)
171>>> q/t
172Equation(c, 1/c)
173>>> t**q
174Equation(a**(a*c), (b/c)**(b/c**2))
175
176Utility operations
177>>> t.reversed
178Equation(b/c, a)
179>>> t.swap
180Equation(b/c, a)
181>>> t.lhs
182a
183>>> t.rhs
184b/c
185>>> t.as_Boolean()
186Eq(a, b/c)
187
188`.check()` convenience method for `.as_Boolean().simplify()`
189>>> from sympy import I, pi
190>>> Equation(pi*(I+2), pi*I+2*pi).check()
191True
192>>> Eqn(a,a+1).check()
193False
194
195Differentiation
196Differentiation is applied to both sides if the wrt variable appears on
197both sides.
198>>> q=Eqn(a*c, b/c**2)
199>>> q
200Equation(a*c, b/c**2)
201>>> diff(q,b)
202Equation(Derivative(a*c, b), c**(-2))
203>>> diff(q,c)
204Equation(a, -2*b/c**3)
205>>> diff(log(q),b)
206Equation(Derivative(log(a*c), b), 1/b)
207>>> diff(q,c,2)
208Equation(Derivative(a, c), 6*b/c**4)
209
210If you specify multiple differentiation all at once the assumption
211is order of differentiation matters and the lhs will not be
212evaluated.
213>>> diff(q,c,b)
214Equation(Derivative(a*c, b, c), -2/c**3)
215
216To overcome this specify the order of operations.
217>>> diff(diff(q,c),b)
218Equation(Derivative(a, b), -2/c**3)
219
220But the reverse order returns an unevaulated lhs (a may depend on b).
221>>> diff(diff(q,b),c)
222Equation(Derivative(a*c, b, c), -2/c**3)
223
224Integration can only be performed on one side at a time.
225>>> q=Eqn(a*c,b/c)
226>>> integrate(q,b,side='rhs')
227b**2/(2*c)
228>>> integrate(q,b,side='lhs')
229a*b*c
230
231Make a pretty statement of integration from an equation
232>>> Eqn(Integral(q.lhs,b),integrate(q,b,side='rhs'))
233Equation(Integral(a*c, b), b**2/(2*c))
234
235Integration of each side with respect to different variables
236>>> q.dorhs.integrate(b).dolhs.integrate(a)
237Equation(a**2*c/2, b**2/(2*c))
238
239Automatic solutions using sympy solvers. THIS IS EXPERIMENTAL. Please
240report issues at https://github.com/gutow/Algebra_with_Sympy/issues.
241>>> tosolv = Eqn(a - b, c/a)
242>>> solve(tosolv,a)
243FiniteSet(Equation(a, b/2 - sqrt(b**2 + 4*c)/2), Equation(a, b/2 + sqrt(b**2 + 4*c)/2))
244>>> solve(tosolv, b)
245FiniteSet(Equation(b, (a**2 - c)/a))
246>>> solve(tosolv, c)
247FiniteSet(Equation(c, a**2 - a*b))
248"""
249import sys
250
251import sympy
252from algebra_with_sympy.preparser import integers_as_exact
253from sympy import *
254
255class algwsym_config():
256
257    def __init__(self):
258        """
259        This is a class to hold parameters that control behavior of
260        the algebra_with_sympy package.
261
262        Settings
263        ========
264        Printing
265        --------
266        In interactive environments the default output of an equation is a
267        human readable string with the two sides connected by an equals
268        sign or a typeset equation with the two sides connected by an equals sign.
269        `print(Eqn)` or `str(Eqn)` will return this human readable text version of
270        the equation as well. This is consistent with python standards, but not
271        sympy, where `str()` is supposed to return something that can be
272        copy-pasted into code. If the equation has a declared name as in `eq1 =
273        Eqn(a,b/c)` the name will be displayed to the right of the equation in
274        parentheses (eg. `a = b/c    (eq1)`). Use `print(repr(Eqn))` instead of
275        `print(Eqn)` or `repr(Eqn)` instead of `str(Eqn)` to get a code
276        compatible version of the equation.
277
278        You can adjust this behavior using some flags that impact output:
279        * `algwsym_config.output.show_code` default is `False`.
280        * `algwsym_config.output.human_text` default is `True`.
281        * `algwsym_config.output.label` default is `True`.
282        * `algwsym_config.output.latex_as_equations` default is `False`
283
284        In interactive environments you can get both types of output by setting
285        the `algwsym_config.output.show_code` flag. If this flag is true
286        calls to `latex` and `str` will also print an additional line "code
287        version: `repr(Eqn)`". Thus in Jupyter you will get a line of typeset
288        mathematics output preceded by the code version that can be copy-pasted.
289        Default is `False`.
290
291        A second flag `algwsym_config.output.human_text` is useful in
292        text-based interactive environments such as command line python or
293        ipython. If this flag is true `repr` will return `str`. Thus the human
294        readable text will be printed as the output of a line that is an
295        expression containing an equation.
296        Default is `True`.
297
298        Setting both of these flags to true in a command line or ipython
299        environment will show both the code version and the human readable text.
300        These flags impact the behavior of the `print(Eqn)` statement.
301
302        The third flag `algwsym_config.output.label` has a default value of
303        `True`. Setting this to `False` suppresses the labeling of an equation
304        with its python name off to the right of the equation.
305
306        The fourth flag `algwsym_config.output.latex_as_equations` has
307        a default value of `False`. Setting this to `True` wraps
308        output as LaTex equations wrapping them in `\\begin{equation}...\\end{
309        equation}`.
310        """
311        pass
312
313    class output():
314
315        def __init__(self):
316            """This holds settings that impact output.
317            """
318            pass
319
320        @property
321        def show_code(self):
322            """
323            If `True` code versions of the equation expression will be
324            output in interactive environments. Default = `False`.
325            """
326            return self.show_code
327
328        @property
329        def human_text(self):
330            """
331            If `True` the human readable equation expression will be
332            output in text interactive environments. Default = `False`.
333            """
334            return self.human_text
335
336        @property
337        def solve_to_list(self):
338            """
339            If `True` the results of a call to `solve(...)` will return a
340            Python `list` rather than a Sympy `FiniteSet`. This recovers
341            behavior for versions before 0.11.0.
342
343            Note: setting this `True` means that expressions within the
344            returned solutions will not be pretty-printed in Jupyter and
345            IPython.
346            """
347            return self.solve_to_list
348
349        @property
350        def latex_as_equations(self):
351            """
352            If `True` any output that is returned as LaTex for
353            pretty-printing will be wrapped in the formal Latex for an
354            equation. For example rather than
355            ```
356            $\\frac{a}{b}=c$
357            ```
358            the output will be
359            ```
360            $$
361            \\begin{equation}\\frac{a}{b}=c\\end{equation}
362            $$
363            ```
364            """
365            return self.latex_as_equation
366
367    class numerics():
368
369        def __init__(self):
370            """This class holds settings for how numerical computation and
371            inputs are handled.
372            """
373            pass
374
375        def integers_as_exact(self):
376            """**This is a flag for informational purposes and interface
377            consistency. Changing the value will not change the behavior.**
378
379            To change the behavior call:
380            * `unset_integers_as_exact()` to turn this feature off.
381            * `set_integers_as_exact()` to turn this feature on (on by
382            default).
383
384            If set to `True` (the default) and if running in an
385            IPython/Jupyter environment any number input without a decimal
386            will be interpreted as a sympy integer. Thus, fractions and
387            related expressions will not evalute to floating point numbers,
388            but be maintained as exact expressions (e.g. 2/3 -> 2/3 not the
389            float 0.6666...).
390            """
391            return self.integers_as_exact
392
393def __latex_override__(expr, *arg):
394    from IPython import get_ipython
395    show_code = False
396    latex_as_equations = False
397    if get_ipython():
398        algwsym_config = get_ipython().user_ns.get("algwsym_config", False)
399    else:
400        algwsym_config = globals()['algwsym_config']
401    if algwsym_config:
402        show_code = algwsym_config.output.show_code
403        latex_as_equations = algwsym_config.output.latex_as_equations
404    if show_code:
405        print("Code version: " + repr(expr))
406    if latex_as_equations:
407        return '$$\\begin{equation}'+latex(expr)+'\\end{equation}$$'
408    else:
409        tempstr = ''
410        namestr = ''
411        if isinstance(expr, Equation):
412            namestr = expr._get_eqn_name()
413        if namestr != '' and algwsym_config.output.label:
414            tempstr += '\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,'
415            tempstr += '(\\text{' + namestr + '})'
416        return '$'+latex(expr) + tempstr + '$'
417
418def __command_line_printing__(expr, *arg):
419    # print('Entering __command_line_printing__')
420    human_text = True
421    show_code = False
422    if algwsym_config:
423        human_text = algwsym_config.output.human_text
424        show_code = algwsym_config.output.show_code
425    tempstr = ''
426    if show_code:
427        tempstr += "Code version: " + repr(expr) + '\n'
428    if not human_text:
429        return print(tempstr + repr(expr))
430    else:
431        labelstr = ''
432        namestr = ''
433        if isinstance(expr, Equation):
434            namestr = expr._get_eqn_name()
435        if namestr != '' and algwsym_config.output.label:
436            labelstr += '          (' + namestr + ')'
437        return print(tempstr + str(expr) + labelstr)
438
439# Now we inject the formatting override(s)
440from IPython import get_ipython
441ip = get_ipython()
442formatter = None
443if ip:
444    # In an environment that can display typeset latex
445    formatter = ip.display_formatter
446    old = formatter.formatters['text/latex'].for_type(Basic,
447                                                      __latex_override__)
448    # print("For type Basic overriding latex formatter = " + str(old))
449
450    # For the terminal based IPython
451    if "text/latex" not in formatter.active_types:
452        old = formatter.formatters['text/plain'].for_type(tuple,
453                                                    __command_line_printing__)
454        # print("For type tuple overriding plain text formatter = " + str(old))
455        for k in sympy.__all__:
456            if k in globals() and not "Printer" in k:
457                if isinstance(globals()[k], type):
458                    old = formatter.formatters['text/plain'].\
459                        for_type(globals()[k], __command_line_printing__)
460                    # print("For type "+str(k)+
461                    # " overriding plain text formatter = " + str(old))
462else:
463    # command line
464    # print("Overriding command line printing of python.")
465    sys.displayhook = __command_line_printing__
466
467# Numerics controls
468def set_integers_as_exact():
469    """This operation uses `sympy.interactive.session.int_to_Integer`, which
470    causes any number input without a decimal to be interpreted as a sympy
471    integer, to pre-parse input cells. It also sets the flag
472    `algwsym_config.numerics.integers_as_exact = True` This is the default
473    mode of algebra_with_sympy. To turn this off call
474    `unset_integers_as_exact()`.
475    """
476    from IPython import get_ipython
477    if get_ipython():
478        get_ipython().input_transformers_post.append(integers_as_exact)
479        algwsym_config = get_ipython().user_ns.get("algwsym_config", False)
480        if algwsym_config:
481            algwsym_config.numerics.integers_as_exact = True
482        else:
483            raise ValueError("The algwsym_config object does not exist.")
484    return
485
486def unset_integers_as_exact():
487    """This operation disables forcing of numbers input without
488    decimals being interpreted as sympy integers. Numbers input without a
489    decimal may be interpreted as floating point if they are part of an
490    expression that undergoes python evaluation (e.g. 2/3 -> 0.6666...). It
491    also sets the flag `algwsym_config.numerics.integers_as_exact = False`.
492    Call `set_integers_as_exact()` to avoid this conversion of rational
493    fractions and related expressions to floating point. Algebra_with_sympy
494    starts with `set_integers_as_exact()` enabled (
495    `algwsym_config.numerics.integers_as_exact = True`).
496    """
497    from IPython import get_ipython
498    if get_ipython():
499        pre = get_ipython().input_transformers_post
500        # The below looks excessively complicated, but more reliably finds the
501        # transformer to remove across varying IPython environments.
502        for k in pre:
503            if "integers_as_exact" in k.__name__:
504                pre.remove(k)
505        algwsym_config = get_ipython().user_ns.get("algwsym_config", False)
506        if algwsym_config:
507            algwsym_config.numerics.integers_as_exact = False
508        else:
509            raise ValueError("The algwsym_config object does not exist.")
510
511    return
512
513Eqn = Equation
514if ip and "text/latex" not in formatter.active_types:
515    old = formatter.formatters['text/plain'].for_type(Eqn,
516                                                __command_line_printing__)
517    # print("For type Equation overriding plain text formatter = " + str(old))
518
519def units(names):
520    """
521    This operation declares the symbols to be positive values, so that sympy will handle them properly
522    when simplifying expressions containing units.
523
524    :param string names: a string containing a space separated list of symbols to be treated as units.
525
526    :return string list of defined units: calls `name = symbols(name,
527    positive=True)` in the interactive namespace for each symbol name.
528    """
529    from sympy.core.symbol import symbols
530    #import __main__ as shell
531    from IPython import get_ipython
532    syms = names.split(' ')
533    user_namespace = None
534    retstr = ''
535    if get_ipython():
536        user_namespace = get_ipython().user_ns
537    else:
538        import sys
539        frame_num = 0
540        frame_name = None
541        while frame_name != '__main__' and frame_num < 50:
542            user_namespace = sys._getframe(frame_num).f_globals
543            frame_num +=1
544            frame_name = user_namespace['__name__']
545    retstr +='('
546    for k in syms:
547        user_namespace[k] = symbols(k, positive = True)
548        retstr += k + ','
549    retstr = retstr[:-1] + ')'
550    return retstr
551
552
553def solve(f, *symbols, **flags):
554    """
555    Override of sympy `solve()`.
556
557    If passed an expression and variable(s) to solve for it behaves
558    almost the same as normal solve with `dict = True`, except that solutions
559    are wrapped in a FiniteSet() to guarantee that the output will be pretty
560    printed in Jupyter like environments.
561
562    If passed an equation or equations it returns solutions as a
563    `FiniteSet()` of solutions, where each solution is represented by an
564    equation or set of equations.
565
566    To get a Python `list` of solutions (pre-0.11.0 behavior) rather than a
567    `FiniteSet` issue the command `algwsym_config.output.solve_to_list = True`.
568    This also prevents pretty-printing in IPython and Jupyter.
569
570    Examples
571    --------
572    >>> a, b, c, x, y = symbols('a b c x y', real = True)
573    >>> import sys
574    >>> sys.displayhook = __command_line_printing__ # set by default on normal initialization.
575    >>> eq1 = Eqn(abs(2*x+y),3)
576    >>> eq2 = Eqn(abs(x + 2*y),3)
577    >>> B = solve((eq1,eq2))
578
579    Default human readable output on command line
580    >>> B
581    {{x = -3, y = 3}, {x = -1, y = -1}, {x = 1, y = 1}, {x = 3, y = -3}}
582
583    To get raw output turn off by setting
584    >>> algwsym_config.output.human_text=False
585    >>> B
586    FiniteSet(FiniteSet(Equation(x, -3), Equation(y, 3)), FiniteSet(Equation(x, -1), Equation(y, -1)), FiniteSet(Equation(x, 1), Equation(y, 1)), FiniteSet(Equation(x, 3), Equation(y, -3)))
587
588    Pre-0.11.0 behavior where a python list of solutions is returned
589    >>> algwsym_config.output.solve_to_list = True
590    >>> solve((eq1,eq2))
591    [[Equation(x, -3), Equation(y, 3)], [Equation(x, -1), Equation(y, -1)], [Equation(x, 1), Equation(y, 1)], [Equation(x, 3), Equation(y, -3)]]
592    >>> algwsym_config.output.solve_to_list = False # reset to default
593
594    `algwsym_config.output.human_text = True` with
595    `algwsym_config.output.how_code=True` shows both.
596    In Jupyter-like environments `show_code=True` yields the Raw output and
597    a typeset version. If `show_code=False` (the default) only the
598    typeset version is shown in Jupyter.
599    >>> algwsym_config.output.show_code=True
600    >>> algwsym_config.output.human_text=True
601    >>> B
602    Code version: FiniteSet(FiniteSet(Equation(x, -3), Equation(y, 3)), FiniteSet(Equation(x, -1), Equation(y, -1)), FiniteSet(Equation(x, 1), Equation(y, 1)), FiniteSet(Equation(x, 3), Equation(y, -3)))
603    {{x = -3, y = 3}, {x = -1, y = -1}, {x = 1, y = 1}, {x = 3, y = -3}}
604    """
605    from sympy.solvers.solvers import solve
606    from sympy.sets.sets import FiniteSet
607    from IPython.display import display
608    newf =[]
609    solns = []
610    displaysolns = []
611    contains_eqn = False
612    if hasattr(f,'__iter__'):
613        for k in f:
614            if isinstance(k, Equation):
615                newf.append(k.lhs-k.rhs)
616                contains_eqn = True
617            else:
618                newf.append(k)
619    else:
620        if isinstance(f, Equation):
621            newf.append(f.lhs - f.rhs)
622            contains_eqn = True
623        else:
624            newf.append(f)
625    flags['dict'] = True
626    result = solve(newf, *symbols, **flags)
627    if contains_eqn:
628        if len(result[0]) == 1:
629            for k in result:
630                for key in k.keys():
631                    val = k[key]
632                    tempeqn = Eqn(key, val)
633                    solns.append(tempeqn)
634        else:
635            for k in result:
636                solnset = []
637                for key in k.keys():
638                    val = k[key]
639                    tempeqn = Eqn(key, val)
640                    solnset.append(tempeqn)
641                if not algwsym_config.output.solve_to_list:
642                    solnset = FiniteSet(*solnset)
643                solns.append(solnset)
644    else:
645        solns = result
646    if algwsym_config.output.solve_to_list:
647        return list(solns)
648    else:
649        return FiniteSet(*solns)
650
651def solveset(f, symbols, domain=sympy.Complexes):
652    """
653    Very experimental override of sympy solveset, which we hope will replace
654    solve. Much is not working. It is not clear how to input a system of
655    equations unless you directly select `linsolve`, etc...
656    """
657    from sympy.solvers import solveset as solve
658    from IPython.display import display
659    newf = []
660    solns = []
661    displaysolns = []
662    contains_eqn = False
663    if hasattr(f, '__iter__'):
664        for k in f:
665            if isinstance(k, Equation):
666                newf.append(k.lhs - k.rhs)
667                contains_eqn = True
668            else:
669                newf.append(k)
670    else:
671        if isinstance(f, Equation):
672            newf.append(f.lhs - f.rhs)
673            contains_eqn = True
674        else:
675            newf.append(f)
676    result = solve(*newf, symbols, domain=domain)
677    # if contains_eqn:
678    #     if len(result[0]) == 1:
679    #         for k in result:
680    #             for key in k.keys():
681    #                 val = k[key]
682    #                 tempeqn = Eqn(key, val)
683    #                 solns.append(tempeqn)
684    #         display(*solns)
685    #     else:
686    #         for k in result:
687    #             solnset = []
688    #             displayset = []
689    #             for key in k.keys():
690    #                 val = k[key]
691    #                 tempeqn = Eqn(key, val)
692    #                 solnset.append(tempeqn)
693    #                 if algwsym_config.output.show_solve_output:
694    #                     displayset.append(tempeqn)
695    #             if algwsym_config.output.show_solve_output:
696    #                 displayset.append('-----')
697    #             solns.append(solnset)
698    #             if algwsym_config.output.show_solve_output:
699    #                 for k in displayset:
700    #                     displaysolns.append(k)
701    #         if algwsym_config.output.show_solve_output:
702    #             display(*displaysolns)
703    # else:
704    solns = result
705    return solns
706
707
708class Equality(Equality):
709    """
710    Extension of Equality class to include the ability to convert it to an
711    Equation.
712    """
713    def to_Equation(self):
714        """
715        Return: recasts the Equality as an Equation.
716        """
717        return Equation(self.lhs,self.rhs)
718
719    def to_Eqn(self):
720        """
721        Synonym for to_Equation.
722        Return: recasts the Equality as an Equation.
723        """
724        return self.to_Equation()
725
726Eq = Equality
727
728def __FiniteSet__repr__override__(self):
729    """Override of the `FiniteSet.__repr__(self)` to overcome sympy's
730    inconsistent wrapping of Finite Sets which prevents reliable use of
731    copy and paste of the code representation.
732    """
733    insidestr = ""
734    for k in self.args:
735        insidestr += k.__repr__() +', '
736    insidestr = insidestr[:-2]
737    reprstr = "FiniteSet("+ insidestr + ")"
738    return reprstr
739
740sympy.sets.FiniteSet.__repr__ = __FiniteSet__repr__override__
741
742def __FiniteSet__str__override__(self):
743    """Override of the `FiniteSet.__str__(self)` to overcome sympy's
744    inconsistent wrapping of Finite Sets which prevents reliable use of
745    copy and paste of the code representation.
746    """
747    insidestr = ""
748    for k in self.args:
749        insidestr += str(k) + ', '
750    insidestr = insidestr[:-2]
751    strrep = "{"+ insidestr + "}"
752    return strrep
753
754sympy.sets.FiniteSet.__str__ = __FiniteSet__str__override__
755
756# Redirect python abs() to Abs()
757abs = Abs
class algwsym_config:
256class algwsym_config():
257
258    def __init__(self):
259        """
260        This is a class to hold parameters that control behavior of
261        the algebra_with_sympy package.
262
263        Settings
264        ========
265        Printing
266        --------
267        In interactive environments the default output of an equation is a
268        human readable string with the two sides connected by an equals
269        sign or a typeset equation with the two sides connected by an equals sign.
270        `print(Eqn)` or `str(Eqn)` will return this human readable text version of
271        the equation as well. This is consistent with python standards, but not
272        sympy, where `str()` is supposed to return something that can be
273        copy-pasted into code. If the equation has a declared name as in `eq1 =
274        Eqn(a,b/c)` the name will be displayed to the right of the equation in
275        parentheses (eg. `a = b/c    (eq1)`). Use `print(repr(Eqn))` instead of
276        `print(Eqn)` or `repr(Eqn)` instead of `str(Eqn)` to get a code
277        compatible version of the equation.
278
279        You can adjust this behavior using some flags that impact output:
280        * `algwsym_config.output.show_code` default is `False`.
281        * `algwsym_config.output.human_text` default is `True`.
282        * `algwsym_config.output.label` default is `True`.
283        * `algwsym_config.output.latex_as_equations` default is `False`
284
285        In interactive environments you can get both types of output by setting
286        the `algwsym_config.output.show_code` flag. If this flag is true
287        calls to `latex` and `str` will also print an additional line "code
288        version: `repr(Eqn)`". Thus in Jupyter you will get a line of typeset
289        mathematics output preceded by the code version that can be copy-pasted.
290        Default is `False`.
291
292        A second flag `algwsym_config.output.human_text` is useful in
293        text-based interactive environments such as command line python or
294        ipython. If this flag is true `repr` will return `str`. Thus the human
295        readable text will be printed as the output of a line that is an
296        expression containing an equation.
297        Default is `True`.
298
299        Setting both of these flags to true in a command line or ipython
300        environment will show both the code version and the human readable text.
301        These flags impact the behavior of the `print(Eqn)` statement.
302
303        The third flag `algwsym_config.output.label` has a default value of
304        `True`. Setting this to `False` suppresses the labeling of an equation
305        with its python name off to the right of the equation.
306
307        The fourth flag `algwsym_config.output.latex_as_equations` has
308        a default value of `False`. Setting this to `True` wraps
309        output as LaTex equations wrapping them in `\\begin{equation}...\\end{
310        equation}`.
311        """
312        pass
313
314    class output():
315
316        def __init__(self):
317            """This holds settings that impact output.
318            """
319            pass
320
321        @property
322        def show_code(self):
323            """
324            If `True` code versions of the equation expression will be
325            output in interactive environments. Default = `False`.
326            """
327            return self.show_code
328
329        @property
330        def human_text(self):
331            """
332            If `True` the human readable equation expression will be
333            output in text interactive environments. Default = `False`.
334            """
335            return self.human_text
336
337        @property
338        def solve_to_list(self):
339            """
340            If `True` the results of a call to `solve(...)` will return a
341            Python `list` rather than a Sympy `FiniteSet`. This recovers
342            behavior for versions before 0.11.0.
343
344            Note: setting this `True` means that expressions within the
345            returned solutions will not be pretty-printed in Jupyter and
346            IPython.
347            """
348            return self.solve_to_list
349
350        @property
351        def latex_as_equations(self):
352            """
353            If `True` any output that is returned as LaTex for
354            pretty-printing will be wrapped in the formal Latex for an
355            equation. For example rather than
356            ```
357            $\\frac{a}{b}=c$
358            ```
359            the output will be
360            ```
361            $$
362            \\begin{equation}\\frac{a}{b}=c\\end{equation}
363            $$
364            ```
365            """
366            return self.latex_as_equation
367
368    class numerics():
369
370        def __init__(self):
371            """This class holds settings for how numerical computation and
372            inputs are handled.
373            """
374            pass
375
376        def integers_as_exact(self):
377            """**This is a flag for informational purposes and interface
378            consistency. Changing the value will not change the behavior.**
379
380            To change the behavior call:
381            * `unset_integers_as_exact()` to turn this feature off.
382            * `set_integers_as_exact()` to turn this feature on (on by
383            default).
384
385            If set to `True` (the default) and if running in an
386            IPython/Jupyter environment any number input without a decimal
387            will be interpreted as a sympy integer. Thus, fractions and
388            related expressions will not evalute to floating point numbers,
389            but be maintained as exact expressions (e.g. 2/3 -> 2/3 not the
390            float 0.6666...).
391            """
392            return self.integers_as_exact
algwsym_config()
258    def __init__(self):
259        """
260        This is a class to hold parameters that control behavior of
261        the algebra_with_sympy package.
262
263        Settings
264        ========
265        Printing
266        --------
267        In interactive environments the default output of an equation is a
268        human readable string with the two sides connected by an equals
269        sign or a typeset equation with the two sides connected by an equals sign.
270        `print(Eqn)` or `str(Eqn)` will return this human readable text version of
271        the equation as well. This is consistent with python standards, but not
272        sympy, where `str()` is supposed to return something that can be
273        copy-pasted into code. If the equation has a declared name as in `eq1 =
274        Eqn(a,b/c)` the name will be displayed to the right of the equation in
275        parentheses (eg. `a = b/c    (eq1)`). Use `print(repr(Eqn))` instead of
276        `print(Eqn)` or `repr(Eqn)` instead of `str(Eqn)` to get a code
277        compatible version of the equation.
278
279        You can adjust this behavior using some flags that impact output:
280        * `algwsym_config.output.show_code` default is `False`.
281        * `algwsym_config.output.human_text` default is `True`.
282        * `algwsym_config.output.label` default is `True`.
283        * `algwsym_config.output.latex_as_equations` default is `False`
284
285        In interactive environments you can get both types of output by setting
286        the `algwsym_config.output.show_code` flag. If this flag is true
287        calls to `latex` and `str` will also print an additional line "code
288        version: `repr(Eqn)`". Thus in Jupyter you will get a line of typeset
289        mathematics output preceded by the code version that can be copy-pasted.
290        Default is `False`.
291
292        A second flag `algwsym_config.output.human_text` is useful in
293        text-based interactive environments such as command line python or
294        ipython. If this flag is true `repr` will return `str`. Thus the human
295        readable text will be printed as the output of a line that is an
296        expression containing an equation.
297        Default is `True`.
298
299        Setting both of these flags to true in a command line or ipython
300        environment will show both the code version and the human readable text.
301        These flags impact the behavior of the `print(Eqn)` statement.
302
303        The third flag `algwsym_config.output.label` has a default value of
304        `True`. Setting this to `False` suppresses the labeling of an equation
305        with its python name off to the right of the equation.
306
307        The fourth flag `algwsym_config.output.latex_as_equations` has
308        a default value of `False`. Setting this to `True` wraps
309        output as LaTex equations wrapping them in `\\begin{equation}...\\end{
310        equation}`.
311        """
312        pass

This is a class to hold parameters that control behavior of the algebra_with_sympy package.

Settings

Printing

In interactive environments the default output of an equation is a human readable string with the two sides connected by an equals sign or a typeset equation with the two sides connected by an equals sign. print(Eqn) or str(Eqn) will return this human readable text version of the equation as well. This is consistent with python standards, but not sympy, where str() is supposed to return something that can be copy-pasted into code. If the equation has a declared name as in eq1 = Eqn(a,b/c) the name will be displayed to the right of the equation in parentheses (eg. a = b/c (eq1)). Use print(repr(Eqn)) instead of print(Eqn) or repr(Eqn) instead of str(Eqn) to get a code compatible version of the equation.

You can adjust this behavior using some flags that impact output:

In interactive environments you can get both types of output by setting the algwsym_config.output.show_code flag. If this flag is true calls to latex and str will also print an additional line "code version: repr(Eqn)". Thus in Jupyter you will get a line of typeset mathematics output preceded by the code version that can be copy-pasted. Default is False.

A second flag algwsym_config.output.human_text is useful in text-based interactive environments such as command line python or ipython. If this flag is true repr will return str. Thus the human readable text will be printed as the output of a line that is an expression containing an equation. Default is True.

Setting both of these flags to true in a command line or ipython environment will show both the code version and the human readable text. These flags impact the behavior of the print(Eqn) statement.

The third flag algwsym_config.output.label has a default value of True. Setting this to False suppresses the labeling of an equation with its python name off to the right of the equation.

The fourth flag algwsym_config.output.latex_as_equations has a default value of False. Setting this to True wraps output as LaTex equations wrapping them in \begin{equation}...\end{ equation}.

class algwsym_config.output:
314    class output():
315
316        def __init__(self):
317            """This holds settings that impact output.
318            """
319            pass
320
321        @property
322        def show_code(self):
323            """
324            If `True` code versions of the equation expression will be
325            output in interactive environments. Default = `False`.
326            """
327            return self.show_code
328
329        @property
330        def human_text(self):
331            """
332            If `True` the human readable equation expression will be
333            output in text interactive environments. Default = `False`.
334            """
335            return self.human_text
336
337        @property
338        def solve_to_list(self):
339            """
340            If `True` the results of a call to `solve(...)` will return a
341            Python `list` rather than a Sympy `FiniteSet`. This recovers
342            behavior for versions before 0.11.0.
343
344            Note: setting this `True` means that expressions within the
345            returned solutions will not be pretty-printed in Jupyter and
346            IPython.
347            """
348            return self.solve_to_list
349
350        @property
351        def latex_as_equations(self):
352            """
353            If `True` any output that is returned as LaTex for
354            pretty-printing will be wrapped in the formal Latex for an
355            equation. For example rather than
356            ```
357            $\\frac{a}{b}=c$
358            ```
359            the output will be
360            ```
361            $$
362            \\begin{equation}\\frac{a}{b}=c\\end{equation}
363            $$
364            ```
365            """
366            return self.latex_as_equation
algwsym_config.output()
316        def __init__(self):
317            """This holds settings that impact output.
318            """
319            pass

This holds settings that impact output.

show_code = False

If True code versions of the equation expression will be output in interactive environments. Default = False.

human_text = True

If True the human readable equation expression will be output in text interactive environments. Default = False.

solve_to_list = False

If True the results of a call to solve(...) will return a Python list rather than a Sympy FiniteSet. This recovers behavior for versions before 0.11.0.

Note: setting this True means that expressions within the returned solutions will not be pretty-printed in Jupyter and IPython.

latex_as_equations = False

If True any output that is returned as LaTex for pretty-printing will be wrapped in the formal Latex for an equation. For example rather than

$\frac{a}{b}=c$

the output will be

$$
\begin{equation}\frac{a}{b}=c\end{equation}
$$
label = True
class algwsym_config.numerics:
368    class numerics():
369
370        def __init__(self):
371            """This class holds settings for how numerical computation and
372            inputs are handled.
373            """
374            pass
375
376        def integers_as_exact(self):
377            """**This is a flag for informational purposes and interface
378            consistency. Changing the value will not change the behavior.**
379
380            To change the behavior call:
381            * `unset_integers_as_exact()` to turn this feature off.
382            * `set_integers_as_exact()` to turn this feature on (on by
383            default).
384
385            If set to `True` (the default) and if running in an
386            IPython/Jupyter environment any number input without a decimal
387            will be interpreted as a sympy integer. Thus, fractions and
388            related expressions will not evalute to floating point numbers,
389            but be maintained as exact expressions (e.g. 2/3 -> 2/3 not the
390            float 0.6666...).
391            """
392            return self.integers_as_exact
algwsym_config.numerics()
370        def __init__(self):
371            """This class holds settings for how numerical computation and
372            inputs are handled.
373            """
374            pass

This class holds settings for how numerical computation and inputs are handled.

def integers_as_exact(self):
376        def integers_as_exact(self):
377            """**This is a flag for informational purposes and interface
378            consistency. Changing the value will not change the behavior.**
379
380            To change the behavior call:
381            * `unset_integers_as_exact()` to turn this feature off.
382            * `set_integers_as_exact()` to turn this feature on (on by
383            default).
384
385            If set to `True` (the default) and if running in an
386            IPython/Jupyter environment any number input without a decimal
387            will be interpreted as a sympy integer. Thus, fractions and
388            related expressions will not evalute to floating point numbers,
389            but be maintained as exact expressions (e.g. 2/3 -> 2/3 not the
390            float 0.6666...).
391            """
392            return self.integers_as_exact

This is a flag for informational purposes and interface consistency. Changing the value will not change the behavior.

To change the behavior call:

If set to True (the default) and if running in an IPython/Jupyter environment any number input without a decimal will be interpreted as a sympy integer. Thus, fractions and related expressions will not evalute to floating point numbers, but be maintained as exact expressions (e.g. 2/3 -> 2/3 not the float 0.6666...).

ip = None
formatter = None
def set_integers_as_exact():
469def set_integers_as_exact():
470    """This operation uses `sympy.interactive.session.int_to_Integer`, which
471    causes any number input without a decimal to be interpreted as a sympy
472    integer, to pre-parse input cells. It also sets the flag
473    `algwsym_config.numerics.integers_as_exact = True` This is the default
474    mode of algebra_with_sympy. To turn this off call
475    `unset_integers_as_exact()`.
476    """
477    from IPython import get_ipython
478    if get_ipython():
479        get_ipython().input_transformers_post.append(integers_as_exact)
480        algwsym_config = get_ipython().user_ns.get("algwsym_config", False)
481        if algwsym_config:
482            algwsym_config.numerics.integers_as_exact = True
483        else:
484            raise ValueError("The algwsym_config object does not exist.")
485    return

This operation uses sympy.interactive.session.int_to_Integer, which causes any number input without a decimal to be interpreted as a sympy integer, to pre-parse input cells. It also sets the flag algwsym_config.numerics.integers_as_exact = True This is the default mode of algebra_with_sympy. To turn this off call unset_integers_as_exact().

def unset_integers_as_exact():
487def unset_integers_as_exact():
488    """This operation disables forcing of numbers input without
489    decimals being interpreted as sympy integers. Numbers input without a
490    decimal may be interpreted as floating point if they are part of an
491    expression that undergoes python evaluation (e.g. 2/3 -> 0.6666...). It
492    also sets the flag `algwsym_config.numerics.integers_as_exact = False`.
493    Call `set_integers_as_exact()` to avoid this conversion of rational
494    fractions and related expressions to floating point. Algebra_with_sympy
495    starts with `set_integers_as_exact()` enabled (
496    `algwsym_config.numerics.integers_as_exact = True`).
497    """
498    from IPython import get_ipython
499    if get_ipython():
500        pre = get_ipython().input_transformers_post
501        # The below looks excessively complicated, but more reliably finds the
502        # transformer to remove across varying IPython environments.
503        for k in pre:
504            if "integers_as_exact" in k.__name__:
505                pre.remove(k)
506        algwsym_config = get_ipython().user_ns.get("algwsym_config", False)
507        if algwsym_config:
508            algwsym_config.numerics.integers_as_exact = False
509        else:
510            raise ValueError("The algwsym_config object does not exist.")
511
512    return

This operation disables forcing of numbers input without decimals being interpreted as sympy integers. Numbers input without a decimal may be interpreted as floating point if they are part of an expression that undergoes python evaluation (e.g. 2/3 -> 0.6666...). It also sets the flag algwsym_config.numerics.integers_as_exact = False. Call set_integers_as_exact() to avoid this conversion of rational fractions and related expressions to floating point. Algebra_with_sympy starts with set_integers_as_exact() enabled ( algwsym_config.numerics.integers_as_exact = True).

Eqn = <class 'sympy.core.equation.Equation'>
def units(names):
520def units(names):
521    """
522    This operation declares the symbols to be positive values, so that sympy will handle them properly
523    when simplifying expressions containing units.
524
525    :param string names: a string containing a space separated list of symbols to be treated as units.
526
527    :return string list of defined units: calls `name = symbols(name,
528    positive=True)` in the interactive namespace for each symbol name.
529    """
530    from sympy.core.symbol import symbols
531    #import __main__ as shell
532    from IPython import get_ipython
533    syms = names.split(' ')
534    user_namespace = None
535    retstr = ''
536    if get_ipython():
537        user_namespace = get_ipython().user_ns
538    else:
539        import sys
540        frame_num = 0
541        frame_name = None
542        while frame_name != '__main__' and frame_num < 50:
543            user_namespace = sys._getframe(frame_num).f_globals
544            frame_num +=1
545            frame_name = user_namespace['__name__']
546    retstr +='('
547    for k in syms:
548        user_namespace[k] = symbols(k, positive = True)
549        retstr += k + ','
550    retstr = retstr[:-1] + ')'
551    return retstr

This operation declares the symbols to be positive values, so that sympy will handle them properly when simplifying expressions containing units.

Parameters
  • string names: a string containing a space separated list of symbols to be treated as units.
Returns

calls name = symbols(name, positive=True) in the interactive namespace for each symbol name.

def solve(f, *symbols, **flags):
554def solve(f, *symbols, **flags):
555    """
556    Override of sympy `solve()`.
557
558    If passed an expression and variable(s) to solve for it behaves
559    almost the same as normal solve with `dict = True`, except that solutions
560    are wrapped in a FiniteSet() to guarantee that the output will be pretty
561    printed in Jupyter like environments.
562
563    If passed an equation or equations it returns solutions as a
564    `FiniteSet()` of solutions, where each solution is represented by an
565    equation or set of equations.
566
567    To get a Python `list` of solutions (pre-0.11.0 behavior) rather than a
568    `FiniteSet` issue the command `algwsym_config.output.solve_to_list = True`.
569    This also prevents pretty-printing in IPython and Jupyter.
570
571    Examples
572    --------
573    >>> a, b, c, x, y = symbols('a b c x y', real = True)
574    >>> import sys
575    >>> sys.displayhook = __command_line_printing__ # set by default on normal initialization.
576    >>> eq1 = Eqn(abs(2*x+y),3)
577    >>> eq2 = Eqn(abs(x + 2*y),3)
578    >>> B = solve((eq1,eq2))
579
580    Default human readable output on command line
581    >>> B
582    {{x = -3, y = 3}, {x = -1, y = -1}, {x = 1, y = 1}, {x = 3, y = -3}}
583
584    To get raw output turn off by setting
585    >>> algwsym_config.output.human_text=False
586    >>> B
587    FiniteSet(FiniteSet(Equation(x, -3), Equation(y, 3)), FiniteSet(Equation(x, -1), Equation(y, -1)), FiniteSet(Equation(x, 1), Equation(y, 1)), FiniteSet(Equation(x, 3), Equation(y, -3)))
588
589    Pre-0.11.0 behavior where a python list of solutions is returned
590    >>> algwsym_config.output.solve_to_list = True
591    >>> solve((eq1,eq2))
592    [[Equation(x, -3), Equation(y, 3)], [Equation(x, -1), Equation(y, -1)], [Equation(x, 1), Equation(y, 1)], [Equation(x, 3), Equation(y, -3)]]
593    >>> algwsym_config.output.solve_to_list = False # reset to default
594
595    `algwsym_config.output.human_text = True` with
596    `algwsym_config.output.how_code=True` shows both.
597    In Jupyter-like environments `show_code=True` yields the Raw output and
598    a typeset version. If `show_code=False` (the default) only the
599    typeset version is shown in Jupyter.
600    >>> algwsym_config.output.show_code=True
601    >>> algwsym_config.output.human_text=True
602    >>> B
603    Code version: FiniteSet(FiniteSet(Equation(x, -3), Equation(y, 3)), FiniteSet(Equation(x, -1), Equation(y, -1)), FiniteSet(Equation(x, 1), Equation(y, 1)), FiniteSet(Equation(x, 3), Equation(y, -3)))
604    {{x = -3, y = 3}, {x = -1, y = -1}, {x = 1, y = 1}, {x = 3, y = -3}}
605    """
606    from sympy.solvers.solvers import solve
607    from sympy.sets.sets import FiniteSet
608    from IPython.display import display
609    newf =[]
610    solns = []
611    displaysolns = []
612    contains_eqn = False
613    if hasattr(f,'__iter__'):
614        for k in f:
615            if isinstance(k, Equation):
616                newf.append(k.lhs-k.rhs)
617                contains_eqn = True
618            else:
619                newf.append(k)
620    else:
621        if isinstance(f, Equation):
622            newf.append(f.lhs - f.rhs)
623            contains_eqn = True
624        else:
625            newf.append(f)
626    flags['dict'] = True
627    result = solve(newf, *symbols, **flags)
628    if contains_eqn:
629        if len(result[0]) == 1:
630            for k in result:
631                for key in k.keys():
632                    val = k[key]
633                    tempeqn = Eqn(key, val)
634                    solns.append(tempeqn)
635        else:
636            for k in result:
637                solnset = []
638                for key in k.keys():
639                    val = k[key]
640                    tempeqn = Eqn(key, val)
641                    solnset.append(tempeqn)
642                if not algwsym_config.output.solve_to_list:
643                    solnset = FiniteSet(*solnset)
644                solns.append(solnset)
645    else:
646        solns = result
647    if algwsym_config.output.solve_to_list:
648        return list(solns)
649    else:
650        return FiniteSet(*solns)

Override of sympy solve().

If passed an expression and variable(s) to solve for it behaves almost the same as normal solve with dict = True, except that solutions are wrapped in a FiniteSet() to guarantee that the output will be pretty printed in Jupyter like environments.

If passed an equation or equations it returns solutions as a FiniteSet() of solutions, where each solution is represented by an equation or set of equations.

To get a Python list of solutions (pre-0.11.0 behavior) rather than a FiniteSet issue the command algwsym_config.output.solve_to_list = True. This also prevents pretty-printing in IPython and Jupyter.

Examples

>>> a, b, c, x, y = symbols('a b c x y', real = True)
>>> import sys
>>> sys.displayhook = __command_line_printing__ # set by default on normal initialization.
>>> eq1 = Eqn(abs(2*x+y),3)
>>> eq2 = Eqn(abs(x + 2*y),3)
>>> B = solve((eq1,eq2))

Default human readable output on command line

>>> B
{{x = -3, y = 3}, {x = -1, y = -1}, {x = 1, y = 1}, {x = 3, y = -3}}

To get raw output turn off by setting

>>> algwsym_config.output.human_text=False
>>> B
FiniteSet(FiniteSet(Equation(x, -3), Equation(y, 3)), FiniteSet(Equation(x, -1), Equation(y, -1)), FiniteSet(Equation(x, 1), Equation(y, 1)), FiniteSet(Equation(x, 3), Equation(y, -3)))

Pre-0.11.0 behavior where a python list of solutions is returned

>>> algwsym_config.output.solve_to_list = True
>>> solve((eq1,eq2))
[[Equation(x, -3), Equation(y, 3)], [Equation(x, -1), Equation(y, -1)], [Equation(x, 1), Equation(y, 1)], [Equation(x, 3), Equation(y, -3)]]
>>> algwsym_config.output.solve_to_list = False # reset to default

algwsym_config.output.human_text = True with algwsym_config.output.how_code=True shows both. In Jupyter-like environments show_code=True yields the Raw output and a typeset version. If show_code=False (the default) only the typeset version is shown in Jupyter.

>>> algwsym_config.output.show_code=True
>>> algwsym_config.output.human_text=True
>>> B
Code version: FiniteSet(FiniteSet(Equation(x, -3), Equation(y, 3)), FiniteSet(Equation(x, -1), Equation(y, -1)), FiniteSet(Equation(x, 1), Equation(y, 1)), FiniteSet(Equation(x, 3), Equation(y, -3)))
{{x = -3, y = 3}, {x = -1, y = -1}, {x = 1, y = 1}, {x = 3, y = -3}}
def solveset(f, symbols, domain=Complexes):
652def solveset(f, symbols, domain=sympy.Complexes):
653    """
654    Very experimental override of sympy solveset, which we hope will replace
655    solve. Much is not working. It is not clear how to input a system of
656    equations unless you directly select `linsolve`, etc...
657    """
658    from sympy.solvers import solveset as solve
659    from IPython.display import display
660    newf = []
661    solns = []
662    displaysolns = []
663    contains_eqn = False
664    if hasattr(f, '__iter__'):
665        for k in f:
666            if isinstance(k, Equation):
667                newf.append(k.lhs - k.rhs)
668                contains_eqn = True
669            else:
670                newf.append(k)
671    else:
672        if isinstance(f, Equation):
673            newf.append(f.lhs - f.rhs)
674            contains_eqn = True
675        else:
676            newf.append(f)
677    result = solve(*newf, symbols, domain=domain)
678    # if contains_eqn:
679    #     if len(result[0]) == 1:
680    #         for k in result:
681    #             for key in k.keys():
682    #                 val = k[key]
683    #                 tempeqn = Eqn(key, val)
684    #                 solns.append(tempeqn)
685    #         display(*solns)
686    #     else:
687    #         for k in result:
688    #             solnset = []
689    #             displayset = []
690    #             for key in k.keys():
691    #                 val = k[key]
692    #                 tempeqn = Eqn(key, val)
693    #                 solnset.append(tempeqn)
694    #                 if algwsym_config.output.show_solve_output:
695    #                     displayset.append(tempeqn)
696    #             if algwsym_config.output.show_solve_output:
697    #                 displayset.append('-----')
698    #             solns.append(solnset)
699    #             if algwsym_config.output.show_solve_output:
700    #                 for k in displayset:
701    #                     displaysolns.append(k)
702    #         if algwsym_config.output.show_solve_output:
703    #             display(*displaysolns)
704    # else:
705    solns = result
706    return solns

Very experimental override of sympy solveset, which we hope will replace solve. Much is not working. It is not clear how to input a system of equations unless you directly select linsolve, etc...

class Equality(sympy.core.relational.Equality):
709class Equality(Equality):
710    """
711    Extension of Equality class to include the ability to convert it to an
712    Equation.
713    """
714    def to_Equation(self):
715        """
716        Return: recasts the Equality as an Equation.
717        """
718        return Equation(self.lhs,self.rhs)
719
720    def to_Eqn(self):
721        """
722        Synonym for to_Equation.
723        Return: recasts the Equality as an Equation.
724        """
725        return self.to_Equation()

Extension of Equality class to include the ability to convert it to an Equation.

def to_Equation(self):
714    def to_Equation(self):
715        """
716        Return: recasts the Equality as an Equation.
717        """
718        return Equation(self.lhs,self.rhs)

Return: recasts the Equality as an Equation.

def to_Eqn(self):
720    def to_Eqn(self):
721        """
722        Synonym for to_Equation.
723        Return: recasts the Equality as an Equation.
724        """
725        return self.to_Equation()

Synonym for to_Equation. Return: recasts the Equality as an Equation.

default_assumptions = {}
Inherited Members
sympy.core.relational.Equality
rel_op
is_Equality
binary_symbols
integrate
as_poly
sympy.core.relational.Relational
ValidRelationOperator
is_Relational
lhs
rhs
reversed
reversedsign
negated
weak
strict
canonical
equals
expand
sympy.logic.boolalg.Boolean
kind
to_nnf
as_set
sympy.core.basic.Basic
is_number
is_Atom
is_Symbol
is_symbol
is_Indexed
is_Dummy
is_Wild
is_Function
is_Add
is_Mul
is_Pow
is_Number
is_Float
is_Rational
is_Integer
is_NumberSymbol
is_Order
is_Derivative
is_Piecewise
is_Poly
is_AlgebraicNumber
is_Boolean
is_Not
is_Matrix
is_Vector
is_Point
is_MatAdd
is_MatMul
is_real
is_extended_real
is_zero
is_negative
is_commutative
copy
assumptions0
compare
fromiter
class_key
sort_key
dummy_eq
atoms
free_symbols
expr_free_symbols
as_dummy
canonical_variables
rcall
is_hypergeometric
is_comparable
func
args
as_content_primitive
subs
xreplace
has
has_xfree
has_free
replace
find
count
matches
match
count_ops
doit
simplify
refine
rewrite
could_extract_minus_sign
is_odd
is_extended_positive
is_hermitian
is_finite
is_composite
is_nonnegative
is_nonzero
is_irrational
is_extended_negative
is_transcendental
is_positive
is_infinite
is_complex
is_imaginary
is_rational
is_extended_nonzero
is_nonpositive
is_extended_nonnegative
is_polar
is_algebraic
is_even
is_antihermitian
is_extended_nonpositive
is_prime
is_noninteger
is_integer
sympy.core.evalf.EvalfMixin
evalf
n
Eq = <class 'Equality'>
abs = Abs