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
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
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:
algwsym_config.output.show_code
default isFalse
.algwsym_config.output.human_text
default isTrue
.algwsym_config.output.label
default isTrue
.algwsym_config.output.latex_as_equations
default isFalse
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}
.
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
If True
code versions of the equation expression will be
output in interactive environments. Default = False
.
If True
the human readable equation expression will be
output in text interactive environments. Default = 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.
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
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.
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:
unset_integers_as_exact()
to turn this feature off.set_integers_as_exact()
to turn this feature on (on by default).
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...).
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()
.
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
).
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.
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}}
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...
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.
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.
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.
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