From b9aa50787c21d6c1903c4fd85dfad17d582dbcae Mon Sep 17 00:00:00 2001 From: Jonathan Gutow Date: Fri, 1 Dec 2023 08:14:20 -0600 Subject: [PATCH 01/10] Optional formatting as LaTex equations --- algebra_with_sympy/algebraic_equation.py | 33 ++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/algebra_with_sympy/algebraic_equation.py b/algebra_with_sympy/algebraic_equation.py index f995414..82631c0 100644 --- a/algebra_with_sympy/algebraic_equation.py +++ b/algebra_with_sympy/algebraic_equation.py @@ -59,10 +59,11 @@ def __init__(self): `print(Eqn)` or `repr(Eqn)` instead of `str(Eqn)` to get a code compatible version of the equation. - You can adjust this behvior using some flags that impact output: + You can adjust this behavior using some flags that impact output: * `algwsym_config.output.show_code` default is `False`. * `algwsym_config.output.human_text` default is `True`. * `algwsym_config.output.label` default is `True`. + * `algwsym_config.output.latex_as_equations` default is `False` In interactive environments you can get both types of output by setting the `algwsym_config.output.show_code` flag. If this flag is true @@ -85,6 +86,11 @@ def __init__(self): 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 formats + output as LaTex equations wrapping them in `\begin{equation}...\end{ + equation}`. """ pass @@ -124,6 +130,24 @@ def solve_to_list(self): """ return self.solve_to_list + @property + def latex_as_equations(self): + """ + 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} + $$ + ``` + """ + return self.latex_as_equation + class numerics(): def __init__(self): @@ -153,15 +177,20 @@ def integers_as_exact(self): def __latex_override__(expr, *arg): from IPython import get_ipython show_code = False + latex_as_equations = False if get_ipython(): algwsym_config = get_ipython().user_ns.get("algwsym_config", False) else: algwsym_config = globals()['algwsym_config'] if algwsym_config: show_code = algwsym_config.output.show_code + latex_as_equations = algwsym_config.output.latex_as_equations if show_code: print("Code version: " + repr(expr)) - return '$'+latex(expr) + '$' + if latex_as_equations: + return '$$\\begin{equation}'+latex(expr)+'\\end{equation}$$' + else: + return '$'+latex(expr) + '$' def __command_line_printing__(expr, *arg): # print('Entering __command_line_printing__') From 1970283d9a0dbfb97d22cb1a91c6268e293d73c6 Mon Sep 17 00:00:00 2001 From: Jonathan Gutow Date: Fri, 1 Dec 2023 08:15:02 -0600 Subject: [PATCH 02/10] Optional formatting as LaTex equations --- algebra_with_sympy/__init__.py | 1 + version.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/algebra_with_sympy/__init__.py b/algebra_with_sympy/__init__.py index 03267c2..c5500dd 100644 --- a/algebra_with_sympy/__init__.py +++ b/algebra_with_sympy/__init__.py @@ -18,6 +18,7 @@ algwsym_config.output.human_text = True algwsym_config.output.label = True algwsym_config.output.solve_to_list = False +algwsym_config.output.latex_as_equations = False # Set version number for internal access algwsym_version = 'unknown' diff --git a/version.py b/version.py index a514f89..8286a99 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -__version__ = '0.12.0' \ No newline at end of file +__version__ = '0.13.0.dev' \ No newline at end of file From 8da5717f09ef550032ecf4627b0bb426f604dc21 Mon Sep 17 00:00:00 2001 From: Jonathan Gutow Date: Fri, 1 Dec 2023 10:15:31 -0600 Subject: [PATCH 03/10] Optional formatting as LaTex equations --- ReadMe.md | 4 ++++ algebra_with_sympy/algebraic_equation.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index be7b300..b6f16e9 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -137,6 +137,10 @@ github](https://github.com/gutow/Algebra_with_Sympy/issues). ## Change Log +* 0.13.0.dev + * `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}`. * 0.12.0 (July 12, 2023) * Now defaults to interpreting numbers without decimal points as integers. This can be turned off with `unset_integers_as_exact()` and on with diff --git a/algebra_with_sympy/algebraic_equation.py b/algebra_with_sympy/algebraic_equation.py index 82631c0..be8e9bc 100644 --- a/algebra_with_sympy/algebraic_equation.py +++ b/algebra_with_sympy/algebraic_equation.py @@ -88,7 +88,7 @@ def __init__(self): 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 formats + a default value of `False`. Setting this to `True` wraps output as LaTex equations wrapping them in `\begin{equation}...\end{ equation}`. """ From 0da5c72b4484a23ce14cc44dd82f607153e4fe98 Mon Sep 17 00:00:00 2001 From: gutow Date: Fri, 15 Dec 2023 20:24:41 -0600 Subject: [PATCH 04/10] embed sympy trial 1 --- .gitmodules | 3 +++ algebra_with_sympy/sympy | 1 + sympy | 1 + 3 files changed, 5 insertions(+) create mode 100644 .gitmodules create mode 160000 algebra_with_sympy/sympy create mode 160000 sympy diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..acaf045 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "algebra_with_sympy/sympy"] + path = algebra_with_sympy/sympy + url = https://github.com/sympy/sympy.git diff --git a/algebra_with_sympy/sympy b/algebra_with_sympy/sympy new file mode 160000 index 0000000..5804781 --- /dev/null +++ b/algebra_with_sympy/sympy @@ -0,0 +1 @@ +Subproject commit 580478123816a986b27d27f379ad3d0482af2c82 diff --git a/sympy b/sympy new file mode 160000 index 0000000..f5f6c98 --- /dev/null +++ b/sympy @@ -0,0 +1 @@ +Subproject commit f5f6c98bf9ee71c9f3c5569f54d15c4d299049d7 From d25eca8bb5cb6bd499ef47fc26f03d78884427e0 Mon Sep 17 00:00:00 2001 From: gutow Date: Fri, 29 Dec 2023 20:00:18 -0600 Subject: [PATCH 05/10] v1 that works with Equation embedded in custom SymPy. --- algebra_with_sympy/algebraic_equation.py | 1118 +++++----------------- algebra_with_sympy/sympy | 1 - sympy | 1 - 3 files changed, 254 insertions(+), 866 deletions(-) delete mode 160000 algebra_with_sympy/sympy delete mode 160000 sympy diff --git a/algebra_with_sympy/algebraic_equation.py b/algebra_with_sympy/algebraic_equation.py index be8e9bc..6fcdc39 100644 --- a/algebra_with_sympy/algebraic_equation.py +++ b/algebra_with_sympy/algebraic_equation.py @@ -1,21 +1,7 @@ """ -Algebraic Equations with SymPy -============================== - -These tools define relations that all high school and college students would -recognize as mathematical equations. They consist of a left hand side (lhs) -and a right hand side (rhs) connected by a relation operator such as "=". At -present the "=" relation operator is the only option. The relation operator may -not be set. - -This class should not be confused with the Boolean class ``Equality`` -(abbreviated ``Eq``) which specifies that the equality of two objects is -``True``. - -This tool applies operations to both sides of the equation simultaneously, just -as students are taught to do when attempting to isolate (solve for) a -variable. Thus the statement ``Equation/b`` yields a new equation -``Equation.lhs/b = Equation.rhs/b`` +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 @@ -23,17 +9,247 @@ missed details such as a negative sign. This mimics the capabilities available in [SageMath](https://www.sagemath.org/) and [Maxima](http://maxima.sourceforge.net/). + +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)) """ import sys import sympy -from sympy.core.add import _unevaluated_Add -from sympy.core.expr import Expr -from sympy.core.basic import Basic -from sympy.core.evalf import EvalfMixin -from sympy.core.sympify import _sympify from algebra_with_sympy.preparser import integers_as_exact -import functools from sympy import * class algwsym_config(): @@ -190,7 +406,14 @@ def __latex_override__(expr, *arg): if latex_as_equations: return '$$\\begin{equation}'+latex(expr)+'\\end{equation}$$' else: - return '$'+latex(expr) + '$' + tempstr = '' + namestr = '' + if isinstance(expr, Equation): + namestr = expr._get_eqn_name() + if namestr != '' and algwsym_config.output.label: + tempstr += '\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,' + tempstr += '(\\text{' + namestr + '})' + return '$'+latex(expr) + tempstr + '$' def __command_line_printing__(expr, *arg): # print('Entering __command_line_printing__') @@ -205,7 +428,13 @@ def __command_line_printing__(expr, *arg): if not human_text: return print(tempstr + repr(expr)) else: - return print(tempstr + str(expr)) + labelstr = '' + namestr = '' + if isinstance(expr, Equation): + namestr = expr._get_eqn_name() + if namestr != '' and algwsym_config.output.label: + labelstr += ' (' + namestr + ')' + return print(tempstr + str(expr) + labelstr) # Now we inject the formatting override(s) from IPython import get_ipython @@ -281,694 +510,6 @@ def unset_integers_as_exact(): return -class Equation(Basic, EvalfMixin): - """ - This class defines an equation with a left-hand-side (tlhs) and a right- - hand-side (rhs) connected by the "=" operator (e.g. `p*V = n*R*T`). - - 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)) - """ - - def __new__(cls, lhs, rhs, **kwargs): - lhs = _sympify(lhs) - rhs = _sympify(rhs) - if not isinstance(lhs, Expr) or not isinstance(rhs, Expr): - raise TypeError('lhs and rhs must be valid sympy expressions.') - return super().__new__(cls, lhs, rhs) - - def _get_eqn_name(self): - """ - Tries to find the python string name that refers to the equation. In - IPython environments (IPython, Jupyter, etc...) looks in the user_ns. - If not in an IPython environment looks in __main__. - :return: string value if found or empty string. - """ - human_text = algwsym_config.output.human_text - algwsym_config.output.human_text=False - import __main__ as shell - for k in dir(shell): - item = getattr(shell,k) - if isinstance(item,Equation): - if item.__repr__()==self.__repr__() and not \ - k.startswith('_'): - algwsym_config.output.human_text=human_text - return k - algwsym_config.output.human_text = human_text - return '' - - @property - def lhs(self): - """ - Returns the lhs of the equation. - """ - return self.args[0] - - @property - def rhs(self): - """ - Returns the rhs of the equation. - """ - return self.args[1] - - def as_Boolean(self): - """ - Converts the equation to an Equality. - """ - return Equality(self.lhs, self.rhs) - - def check(self, **kwargs): - """ - Forces simplification and casts as `Equality` to check validity. - Parameters - ---------- - kwargs any appropriate for `Equality`. - - Returns - ------- - True, False or an unevaluated `Equality` if truth cannot be determined. - """ - return Equality(self.lhs, self.rhs, **kwargs).simplify() - - @property - def reversed(self): - """ - Swaps the lhs and the rhs. - """ - return Equation(self.rhs, self.lhs) - - @property - def swap(self): - """ - Synonym for `.reversed` - """ - return self.reversed - - def _applytoexpr(self, expr, func, *args, **kwargs): - # Applies a function to an expression checking whether there - # is a specialized version associated with the particular type of - # expression. Errors will be raised if the function cannot be - # applied to an expression. - funcname = getattr(func, '__name__', None) - if funcname is not None: - localfunc = getattr(expr, funcname, None) - if localfunc is not None: - return localfunc(*args, **kwargs) - return func(expr, *args, **kwargs) - - def apply(self, func, *args, side='both', **kwargs): - """ - Apply an operation/function/method to the equation returning the - resulting equation. - - Parameters - ========== - - func: object - object to apply usually a function - - args: as necessary for the function - - side: 'both', 'lhs', 'rhs', optional - Specifies which side of the equation the operation will be applied - to. Default is 'both'. - - kwargs: as necessary for the function - """ - lhs = self.lhs - rhs = self.rhs - if side in ('both', 'lhs'): - lhs = self._applytoexpr(self.lhs, func, *args, **kwargs) - if side in ('both', 'rhs'): - rhs = self._applytoexpr(self.rhs, func, *args, **kwargs) - return Equation(lhs, rhs) - - def applylhs(self, func, *args, **kwargs): - """ - If lhs side of the equation has a defined subfunction (attribute) of - name ``func``, that will be applied instead of the global function. - The operation is applied to only the lhs. - """ - return self.apply(func, *args, **kwargs, side='lhs') - - def applyrhs(self, func, *args, **kwargs): - """ - If rhs side of the equation has a defined subfunction (attribute) of - name ``func``, that will be applied instead of the global function. - The operation is applied to only the rhs. - """ - return self.apply(func, *args, **kwargs, side='rhs') - - class _sides: - """ - Helper class for the `.do.`, `.dolhs.`, `.dorhs.` syntax for applying - submethods of expressions. - """ - - def __init__(self, eqn, side='both'): - self.eqn = eqn - self.side = side - - def __getattr__(self, name): - func = None - if self.side in ('rhs', 'both'): - func = getattr(self.eqn.rhs, name, None) - else: - func = getattr(self.eqn.lhs, name, None) - if func is None: - raise AttributeError('Expressions in the equation have no ' - 'attribute `' + str( - name) + '`. Try `.apply(' - + str(name) + ', *args)` or ' - 'pass the equation as a parameter to `' - + str(name) + '()`.') - return functools.partial(self.eqn.apply, func, side=self.side) - - @property - def do(self): - return self._sides(self, side='both') - - @property - def dolhs(self): - return self._sides(self, side='lhs') - - @property - def dorhs(self): - return self._sides(self, side='rhs') - - def _eval_rewrite(self, rule, args, **kwargs): - """Return Equation(L, R) as Equation(L - R, 0) or as L - R. - - Parameters - ========== - - evaluate : bool, optional - Control the evaluation of the result. If `evaluate=None` then - terms in L and R will not cancel but they will be listed in - canonical order; otherwise non-canonical args will be returned. - Default to True. - - eqn : bool, optional - Control the returned type. If `eqn=True`, then Equation(L - R, 0) - is returned. Otherwise, the L - R symbolic expression is returned. - Default to True. - - Examples - ======== - >>> from sympy import Add - >>> from sympy.abc import b, x - >>> from algebra_with_sympy import Equation - >>> eq = Equation(x + b, x - b) - >>> eq.rewrite(Add) - Equation(2*b, 0) - >>> eq.rewrite(Add, evaluate=None).lhs.args - (b, b, x, -x) - >>> eq.rewrite(Add, evaluate=False).lhs.args - (b, x, b, -x) - >>> eq.rewrite(Add, eqn=False) - 2*b - >>> eq.rewrite(Add, eqn=False, evaluate=False).args - (b, x, b, -x) - """ - if rule == Add: - # NOTE: the code about `evaluate` is very similar to - # sympy.core.relational.Equality._eval_rewrite_as_Add - eqn = kwargs.pop("eqn", True) - evaluate = kwargs.get('evaluate', True) - L, R = args - if evaluate: - # allow cancellation of args - expr = L - R - else: - args = Add.make_args(L) + Add.make_args(-R) - if evaluate is None: - # no cancellation, but canonical - expr = _unevaluated_Add(*args) - else: - # no cancellation, not canonical - expr = Add._from_args(args) - if eqn: - return self.func(expr, 0) - return expr - - def subs(self, *args, **kwargs): - """Substitutes old for new in an equation after sympifying args. - - `args` is either: - - * one or more arguments of type `Equation(old, new)`. - * two arguments, e.g. foo.subs(old, new) - * one iterable argument, e.g. foo.subs(iterable). The iterable may be: - - - an iterable container with (old, new) pairs. In this case the - replacements are processed in the order given with successive - patterns possibly affecting replacements already made. - - a dict or set whose key/value items correspond to old/new pairs. - In this case the old/new pairs will be sorted by op count and in - case of a tie, by number of args and the default_sort_key. The - resulting sorted list is then processed as an iterable container - (see previous). - - If the keyword ``simultaneous`` is True, the subexpressions will not be - evaluated until all the substitutions have been made. - - Please, read ``help(Expr.subs)`` for more examples. - - Examples - ======== - - >>> from sympy.abc import a, b, c, x - >>> from algebra_with_sympy import Equation - >>> eq = Equation(x + a, b * c) - - Substitute a single value: - - >>> eq.subs(b, 4) - Equation(a + x, 4*c) - - Substitute a multiple values: - - >>> eq.subs([(a, 2), (b, 4)]) - Equation(x + 2, 4*c) - >>> eq.subs({a: 2, b: 4}) - Equation(x + 2, 4*c) - - Substitute an equation into another equation: - - >>> eq2 = Equation(x + a, 4) - >>> eq.subs(eq2) - Equation(4, b*c) - - Substitute multiple equations into another equation: - - >>> eq1 = Equation(x + a + b + c, x * a * b * c) - >>> eq2 = Equation(x + a, 4) - >>> eq3 = Equation(b, 5) - >>> eq1.subs(eq2, eq3) - Equation(c + 9, 5*a*c*x) - - """ - new_args = args - if all(isinstance(a, self.func) for a in args): - new_args = [{a.args[0]: a.args[1] for a in args}] - elif (len(args) == 1) and all(isinstance(a, self.func) for a in - args[0]): - raise TypeError("You passed into `subs` a list of elements of " - "type `Equation`, but this is not supported. Please, consider " - "unpacking the list with `.subs(*eq_list)` or select your " - "equations from the list and use `.subs(eq_list[0], eq_list[" - "2], ...)`.") - elif any(isinstance(a, self.func) for a in args): - raise ValueError("`args` contains one or more Equation and some " - "other data type. This mode of operation is not supported. " - "Please, read `subs` documentation to understand how to " - "use it.") - return super().subs(*new_args, **kwargs) - - ##### - # Overrides of binary math operations - ##### - - @classmethod - def _binary_op(cls, a, b, opfunc_ab): - if isinstance(a, Equation) and not isinstance(b, Equation): - return Equation(opfunc_ab(a.lhs, b), opfunc_ab(a.rhs, b)) - elif isinstance(b, Equation) and not isinstance(a, Equation): - return Equation(opfunc_ab(a, b.lhs), opfunc_ab(a, b.rhs)) - elif isinstance(a, Equation) and isinstance(b, Equation): - return Equation(opfunc_ab(a.lhs, b.lhs), opfunc_ab(a.rhs, b.rhs)) - else: - return NotImplemented - - def __add__(self, other): - return self._binary_op(self, other, lambda a, b: a + b) - - def __radd__(self, other): - return self._binary_op(other, self, lambda a, b: a + b) - - def __mul__(self, other): - return self._binary_op(self, other, lambda a, b: a * b) - - def __rmul__(self, other): - return self._binary_op(other, self, lambda a, b: a * b) - - def __sub__(self, other): - return self._binary_op(self, other, lambda a, b: a - b) - - def __rsub__(self, other): - return self._binary_op(other, self, lambda a, b: a - b) - - def __truediv__(self, other): - return self._binary_op(self, other, lambda a, b: a / b) - - def __rtruediv__(self, other): - return self._binary_op(other, self, lambda a, b: a / b) - - def __mod__(self, other): - return self._binary_op(self, other, lambda a, b: a % b) - - def __rmod__(self, other): - return self._binary_op(other, self, lambda a, b: a % b) - - def __pow__(self, other): - return self._binary_op(self, other, lambda a, b: a ** b) - - def __rpow__(self, other): - return self._binary_op(other, self, lambda a, b: a ** b) - - def _eval_power(self, other): - return self.__pow__(other) - - ##### - # Operation helper functions - ##### - def expand(self, *args, **kwargs): - return Equation(self.lhs.expand(*args, **kwargs), self.rhs.expand( - *args, **kwargs)) - - def simplify(self, *args, **kwargs): - return self._eval_simplify(*args, **kwargs) - - def _eval_simplify(self, *args, **kwargs): - return Equation(self.lhs.simplify(*args, **kwargs), self.rhs.simplify( - *args, **kwargs)) - - def _eval_factor(self, *args, **kwargs): - # TODO: cancel out factors common to both sides. - return Equation(self.lhs.factor(*args, **kwargs), self.rhs.factor( - *args, **kwargs)) - - def factor(self, *args, **kwargs): - return self._eval_factor(*args, **kwargs) - - def _eval_collect(self, *args, **kwargs): - from sympy.simplify.radsimp import collect - return Equation(collect(self.lhs, *args, **kwargs), - collect(self.rhs, *args, **kwargs)) - - def collect(self, *args, **kwargs): - return self._eval_collect(*args, **kwargs) - - def evalf(self, *args, **kwargs): - return Equation(self.lhs.evalf(*args, **kwargs), - self.rhs.evalf(*args, **kwargs)) - - n = evalf - - def _eval_derivative(self, *args, **kwargs): - # TODO Find why diff and Derivative do not appear to pass through - # kwargs to this. Since we cannot set evaluation of lhs manually - # try to be intelligent about when to do it. - from sympy.core.function import Derivative - eval_lhs = False - if not (isinstance(self.lhs, Derivative)): - for sym in args: - if sym in self.lhs.free_symbols and not ( - _sympify(sym).is_number): - eval_lhs = True - return Equation(self.lhs.diff(*args, **kwargs, evaluate=eval_lhs), - self.rhs.diff(*args, **kwargs)) - - def _eval_Integral(self, *args, **kwargs): - side = kwargs.pop('side', None) # Could not seem to pass values for - # `evaluate` through to here. - if side is None: - raise ValueError('You must specify `side="lhs"` or `side="rhs"` ' - 'when integrating an Equation') - else: - try: - return (getattr(self, side).integrate(*args, **kwargs)) - except AttributeError: - raise AttributeError('`side` must equal "lhs" or "rhs".') - - ##### - # Output helper functions - ##### - def __repr__(self): - repstr = 'Equation(%s, %s)' %(self.lhs.__repr__(), self.rhs.__repr__()) - # if algwsym_config.output.human_text: - # return self.__str__() - return repstr - - def _latex(self, printer): - tempstr = '' - """ - if algwsym_config.output.show_code and not \ - algwsym_config.output.human_text: - print('code version: '+ self.__repr__()) - """ - tempstr += printer._print(self.lhs) - tempstr += '=' - tempstr += printer._print(self.rhs) - namestr = self._get_eqn_name() - if namestr !='' and algwsym_config.output.label: - tempstr += '\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,' - tempstr += '(\\text{'+namestr+'})' - return tempstr - - def __str__(self): - tempstr = '' - # if algwsym_config.output.show_code: - # human_text = algwsym_config.output.human_text - # algwsym_config.output.human_text=False - # tempstr += '\ncode version: '+self.__repr__() +'\n' - # algwsym_config.output.human_text=human_text - tempstr += str(self.lhs) + ' = ' + str(self.rhs) - namestr = self._get_eqn_name() - if namestr != '' and algwsym_config.output.label: - tempstr += ' (' + namestr + ')' - return tempstr - - Eqn = Equation if ip and "text/latex" not in formatter.active_types: old = formatter.formatters['text/plain'].for_type(Eqn, @@ -1129,76 +670,6 @@ def solveset(f, symbols, domain=sympy.Complexes): solns = result return solns -def sqrt(arg, evaluate = None): - """ - Override of sympy convenience function `sqrt`. Simply divides equations - into two sides if `arg` is an instance of `Equation`. This avoids an - issue with the way sympy is delaying specialized applications of _Pow_ on - objects that are not basic sympy expressions. - """ - from sympy.functions.elementary.miscellaneous import sqrt as symsqrt - if isinstance(arg, Equation): - return Equation(symsqrt(arg.lhs, evaluate), symsqrt(arg.rhs, evaluate)) - else: - return symsqrt(arg,evaluate) - -# Pick up the docstring for sqrt from sympy -from sympy.functions.elementary.miscellaneous import sqrt as symsqrt -sqrt.__doc__+=symsqrt.__doc__ -del symsqrt - -def root(arg, n, k = 0, evaluate = None): - """ - Override of sympy convenience function `root`. Simply divides equations - into two sides if `arg` or `n` is an instance of `Equation`. This - avoids an issue with the way sympy is delaying specialized applications - of _Pow_ on objects that are not basic sympy expressions. - """ - from sympy.functions.elementary.miscellaneous import root as symroot - if isinstance(arg, Equation): - return Equation(symroot(arg.lhs, n, k, evaluate), - symroot(arg.rhs, n, k, evaluate)) - if isinstance(n, Equation): - return Equation(symroot(arg, n.lhs, k, evaluate), - symroot(arg, n.rhs, k, evaluate)) - else: - return symroot(arg, n, k, evaluate) - -# pick up the docstring for root from sympy -from sympy.functions.elementary.miscellaneous import root as symroot -root.__doc__+=symroot.__doc__ -del symroot - -def Heaviside(arg, **kwargs): - """ - Overide of the Heaviside function as implemented in Sympy. Get a recursion - error if use the normal class extension of a function to do this. - - """ - from sympy.functions.special.delta_functions import Heaviside as symHeav - if isinstance(arg, Equation): - return Equation(symHeav((arg.lhs), **kwargs),symHeav((arg.rhs), - **kwargs)) - else: - return symHeav(arg, **kwargs) -# Pick up the docstring for Heaviside from Sympy. -from sympy.functions.special.delta_functions import Heaviside as symHeav -Heaviside.__doc__ += symHeav.__doc__ -del symHeav - -def collect(expr, syms, func=None, evaluate=None, exact=False, - distribute_order_term=True): - """ - Override of sympy `collect()`. - """ - from sympy.simplify.radsimp import collect - _eval_collect = getattr(expr, '_eval_collect', None) - if _eval_collect is not None: - return _eval_collect(syms, func, evaluate, - exact, distribute_order_term) - else: - return collect(expr, syms, func, evaluate, exact, - distribute_order_term) class Equality(Equality): """ @@ -1248,86 +719,5 @@ def __FiniteSet__str__override__(self): sympy.sets.FiniteSet.__str__ = __FiniteSet__str__override__ -##### -# Extension of the Function class. For incorporation into SymPy this should -# become part of the class -##### -class EqnFunction(Function): - """ - Extension of the sympy Function class to understand equations. Each - sympy function impacted by this extension is listed in the documentation - that follows. - """ - def __new__(cls, *args, **kwargs): - n = len(args) - eqnloc = None - neqns = 0 - newargs = [] - for k in args: - newargs.append(k) - if (n > 0): - for i in range(n): - if isinstance(args[i], Equation): - neqns += 1 - eqnloc = i - if neqns > 1: - raise NotImplementedError('Function calls with more than one ' - 'Equation as a parameter are not ' - 'supported. You may be able to get ' - 'your desired outcome using .applyrhs' - ' and .applylhs.') - if neqns == 1: - newargs[eqnloc] = args[eqnloc].lhs - lhs = super().__new__(cls, *newargs, **kwargs) - newargs[eqnloc] = args[eqnloc].rhs - rhs = super().__new__(cls, *newargs, **kwargs) - return Equation(lhs,rhs) - return super().__new__(cls, *args, **kwargs) - -def str_to_extend_sympy_func(func:str): - """ - Generates the string command to execute for a sympy function to - gain the properties of the extended EqnFunction class. - """ - execstr = 'class ' + str(func) + '(' + str( - func) + ',EqnFunction):\n ' \ - 'pass\n' - return execstr - -# TODO: Below will not be needed when incorporated into SymPy. -# This is hacky, but I have not been able to come up with another way -# of extending the functions programmatically, if this is separate package -# from sympy that extends it after loading sympy. -# Functions listed in `skip` are not applicable to equations or cannot be -# extended because of `mro` error or `metaclass conflict`. This reflects -# that some of these are not members of the Sympy Function class. - -# Overridden elsewhere -_extended_ = ('sqrt', 'root', 'Heaviside') - -# Either not applicable to equations or have not yet figured out a way -# to systematically apply to an equation. -# TODO examine these more carefully (top priority: real_root, cbrt, Ynm_c). -_not_applicable_to_equations_ = ('Min', 'Max', 'Id', 'real_root', 'cbrt', - 'unbranched_argument', 'polarify', 'unpolarify', - 'piecewise_fold', 'E1', 'Eijk', 'bspline_basis', - 'bspline_basis_set', 'interpolating_spline', 'jn_zeros', - 'jacobi_normalized', 'Ynm_c', 'piecewise_exclusive', 'Piecewise', - 'motzkin', 'hyper','meijerg', 'chebyshevu_root', 'chebyshevt_root', - 'betainc_regularized') -_skip_ = _extended_ + _not_applicable_to_equations_ - -for func in functions.__all__: - - if func not in _skip_: - try: - exec(str_to_extend_sympy_func(func), globals(), locals()) - except TypeError: - from warnings import warn - warn('SymPy function/operation ' + str(func) + ' may not work ' \ - 'properly with Equations. If you use it with Equations, ' \ - 'validate its behavior. We are working to address this ' \ - 'issue.') - # Redirect python abs() to Abs() abs = Abs \ No newline at end of file diff --git a/algebra_with_sympy/sympy b/algebra_with_sympy/sympy deleted file mode 160000 index 5804781..0000000 --- a/algebra_with_sympy/sympy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 580478123816a986b27d27f379ad3d0482af2c82 diff --git a/sympy b/sympy deleted file mode 160000 index f5f6c98..0000000 --- a/sympy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f5f6c98bf9ee71c9f3c5569f54d15c4d299049d7 From f372883b325c443094de858758a91371a3df47aa Mon Sep 17 00:00:00 2001 From: gutow Date: Fri, 29 Dec 2023 20:59:59 -0600 Subject: [PATCH 06/10] doctests for Eqn embedded in Sympy and issue #23 --- tests/test_algebraic_equation.py | 73 +++++++++++----------- tests/test_extension_of_sympy_functions.py | 48 +++++++------- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/tests/test_algebraic_equation.py b/tests/test_algebraic_equation.py index d914793..ab840e3 100644 --- a/tests/test_algebraic_equation.py +++ b/tests/test_algebraic_equation.py @@ -1,40 +1,34 @@ from sympy import symbols, integrate, simplify, expand, factor, Integral, Add -from sympy import diff, FiniteSet, Equality, Function, functions, Matrix, S +from sympy import diff, FiniteSet, Equation, Function, Matrix, S, Eq from sympy import sin, cos, log, exp, latex, Symbol, I from sympy.core.function import AppliedUndef from sympy.printing.latex import LatexPrinter -from algebra_with_sympy.algebraic_equation import solve, collect, Equation -from algebra_with_sympy.algebraic_equation import Equality, Eq -from algebra_with_sympy.algebraic_equation import Eqn, sqrt, root, Heaviside +from algebra_with_sympy.algebraic_equation import solve, collect +from algebra_with_sympy.algebraic_equation import Equality +from sympy import Eqn, sqrt, root, Heaviside from algebra_with_sympy.algebraic_equation import algwsym_config -from algebra_with_sympy.algebraic_equation import EqnFunction -from algebra_with_sympy.algebraic_equation import _skip_ -from algebra_with_sympy.algebraic_equation import str_to_extend_sympy_func -from pytest import raises -def test_str_to_extend_sympy_func(): - teststr = 'testname' - execstr = 'class %S(%S,EqnFunction):\n pass\n' - execstr = execstr.replace('%S',str(teststr)) - assert str_to_extend_sympy_func(teststr)==execstr - pass +from pytest import raises ##### -# Extension of just the functions used for testing +# Testing that sympy functions work with Equations ##### -for func in ('sin', 'cos', 'log', 'exp'): - if func not in _skip_: - try: - exec(str_to_extend_sympy_func(func), globals(), locals()) - except TypeError: - from warnings import warn - warn('SymPy function/operation ' + str(func) + ' may not work ' \ - 'properly with Equations. If you use it with Equations, ' \ - 'validate its behavior. We are working to address this ' \ - 'issue.') - +# Overridden elsewhere +_extended_ = ('sqrt', 'cbrt', 'root') + +# Either not applicable to Equations or have not yet figured out a way +# to systematically apply to an Equation. +# TODO examine these more carefully (top priority: real_root, Ynm_c). +_not_applicable_to_equations_ = ('Min', 'Max', 'Id', 'real_root', + 'unbranched_argument', 'polarify', 'unpolarify', + 'piecewise_fold', 'E1', 'Eijk', 'bspline_basis', + 'bspline_basis_set', 'interpolating_spline', 'jn_zeros', + 'jacobi_normalized', 'Ynm_c', 'piecewise_exclusive', 'Piecewise', + 'motzkin', 'hyper','meijerg', 'chebyshevu_root', 'chebyshevt_root', + 'betainc_regularized') +_skip_ = _extended_ + _not_applicable_to_equations_ class CustomLatexPrinter(LatexPrinter): """Print undefined applied functions without arguments""" @@ -63,7 +57,7 @@ def test_define_equation(): def test_convert_equation(): a, b, c = symbols('a b c') tsteqn = Equation(a, b/c) - assert tsteqn.as_Boolean() == Equality(a, b/c) + assert tsteqn.as_Boolean() == Eq(a, b/c) assert tsteqn.reversed == Equation(b/c, a) assert tsteqn.swap == Equation(b/c, a) @@ -117,12 +111,20 @@ def test_outputs(capsys): import __main__ as gs vars(gs)['tsteqn'] = tsteqn assert tsteqn._get_eqn_name() == 'tsteqn' - assert tsteqn.__str__() == 'a = b/c (tsteqn)' - assert (latex(tsteqn) == - 'a=\\frac{b}{c}\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{tsteqn})') + __command_line_printing__(tsteqn) + captured = capsys.readouterr() + assert captured.out == 'a = b/c (tsteqn)\n' + # make sure sys.displayhook does not point to __command_line_printing__() + import sys + sys.displayhook = sys.__displayhook__ + assert __latex_override__(tsteqn) == ('$a=\\frac{b}{c}\\,\\,\\,\\,\\,\\,' + '\\,\\,\\,\\,(\\text{tsteqn})$') algwsym_config.output.label = False - assert tsteqn.__str__() == 'a = b/c' - assert latex(tsteqn) == 'a=\\frac{b}{c}' + __command_line_printing__(tsteqn) + captured = capsys.readouterr() + assert captured.out == 'a = b/c\n' + assert __latex_override__(tsteqn) == '$a=\\frac{b}{c}$' + algwsym_config.output.label = True f = Function("f")(a, b, c) eq = Eqn(f, 2) @@ -159,9 +161,6 @@ def test_outputs(capsys): 'Equation(y, -1)), FiniteSet(Equation(x, 1), Equation(y, 1)), ' \ 'FiniteSet(Equation(x, 3), Equation(y, -3)))' \ '\n{{x = -3, y = 3}, {x = -1, y = -1}, {x = 1, y = 1}, {x = 3, y = -3}}\n' - # make sure sys.displayhook does not point to __command_line_printing__() - import sys - sys.displayhook = sys.__displayhook__ assert __latex_override__(B) == \ '$\\left\\{\\left\\{x=-3, y=3\\right\\}, \\left\\{x=-1, ' \ 'y=-1\\right\\}, \\left\\{x=1, y=1\\right\\}, ' \ @@ -341,3 +340,7 @@ def test_subs(): assert eq.subs(sd) == Equation(1, b * c) assert eq.subs(sd, simultaneous=True) == Equation(a / (x + a), b * c) +def test_issue_23(): + # This gave a key error + a, t = symbols('a t') + assert simplify(a * cos(t) + sin(t)) == a * cos(t) + sin(t) \ No newline at end of file diff --git a/tests/test_extension_of_sympy_functions.py b/tests/test_extension_of_sympy_functions.py index 29f8efb..d6411ae 100644 --- a/tests/test_extension_of_sympy_functions.py +++ b/tests/test_extension_of_sympy_functions.py @@ -1,31 +1,35 @@ from pytest import raises - -from algebra_with_sympy.algebraic_equation import str_to_extend_sympy_func -from algebra_with_sympy.algebraic_equation import Equation, EqnFunction -from algebra_with_sympy.algebraic_equation import _extended_, _skip_ -from sympy import functions, FunctionClass, symbols +from sympy import functions, FunctionClass, symbols, Equation import importlib -temp = importlib.import_module('sympy', package=functions.__all__) + +##### +# Testing that sympy functions work with Equations +##### + +# Overridden elsewhere +_extended_ = ('sqrt', 'cbrt', 'root') + +# Either not applicable to Equations or have not yet figured out a way +# to systematically apply to an Equation. +# TODO examine these more carefully (top priority: real_root, Ynm_c). +_not_applicable_to_equations_ = ('Min', 'Max', 'Id', 'real_root', + 'unbranched_argument', 'polarify', 'unpolarify', + 'piecewise_fold', 'E1', 'Eijk', 'bspline_basis', + 'bspline_basis_set', 'interpolating_spline', 'jn_zeros', + 'jacobi_normalized', 'Ynm_c', 'piecewise_exclusive', 'Piecewise', + 'motzkin', 'hyper','meijerg', 'chebyshevu_root', 'chebyshevt_root', + 'betainc_regularized') +_skip_ = _extended_ + _not_applicable_to_equations_ + +temp = importlib.import_module('sympy', package=functions) for func in functions.__all__: globals()[func] = getattr(temp, func) -temp = importlib.import_module('algebra_with_sympy.algebraic_equation', - package=_extended_) + # Needed for some tests so that extended functions are in the correct # namespace. for func in _extended_: globals()[func] = getattr(temp, func) -for func in functions.__all__: - if func not in _skip_: - try: - # The string that is executed has a test function below. - exec(str_to_extend_sympy_func(func), globals(), locals()) - except TypeError: - from warnings import warn - warn('SymPy function/operation ' + str(func) + ' may not work ' \ - 'properly with Equations. If you use it with Equations, ' \ - 'validate its behavior. We are working to address this ' \ - 'issue.') def test_sympy_import(): for func in functions.__all__: @@ -34,12 +38,6 @@ def test_sympy_import(): assert(isinstance(globals()[func],FunctionClass)) pass -def test_str_to_extend_sympy_func(): - teststr = 'testname' - execstr = 'class %S(%S,EqnFunction):\n pass\n' - execstr = execstr.replace('%S',str(teststr)) - assert str_to_extend_sympy_func(teststr)==execstr - pass def test_functions_extensions(): from inspect import signature From 5409ce6b4351d7a4b70e3b33f4445b362c3e5c6a Mon Sep 17 00:00:00 2001 From: gutow Date: Mon, 1 Jan 2024 18:02:39 -0600 Subject: [PATCH 07/10] to version 1.0.0rc0. Readme updates. --- ReadMe.md | 17 +++++++++++++++-- setup.py | 2 +- version.py | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index b6f16e9..06ff550 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -105,6 +105,13 @@ $\frac{a}{b} = \frac{c}{d}$ or $e^{\frac{-x^2}{\sigma^2}}$. To also see the * **The equation label** can be turned off by setting `algwsym_config.output.label = False`. +* **Automatic wrapping of `Equations` as Latex equations** can be activated + by setting `algwsym_config.output.latex_as_equations` to `True`. The + default is `False`. Setting this to `True` wraps output as LaTex equations, + wrapping them in \begin{equation}...\end{equation}. Equations formatted + this way will **not** be labeled with the internal name for the equation, + independent of the setting of `algwsym_config.output.label`. + * By default **solutions output by `solve()`** are returned as a SymPy `FiniteSet()` to force typesetting of the included solutions. To get Python lists instead you can override this for the whole session by setting @@ -137,10 +144,16 @@ github](https://github.com/gutow/Algebra_with_Sympy/issues). ## Change Log -* 0.13.0.dev +* 1.0.0rc0 + * Fixed issue #23 where `cos()` multiplied by a factor was not the same + type of object after `simplify()` acted on an expression. Required + embedding the `Equation` type in the sympy library. Until `Equation` is + incorporated into the primary Sympy repository a customized version of + the latest stable release will be used. * `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}`. + in `\begin{equation}...\end{equation}`. Equations formatted this way + will not be labeled with the internal name for the equation. * 0.12.0 (July 12, 2023) * Now defaults to interpreting numbers without decimal points as integers. This can be turned off with `unset_integers_as_exact()` and on with diff --git a/setup.py b/setup.py index 5249485..8df4d24 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ install_requires=[ 'jupyter>=1.0.0', 'jupyterlab>=3.6', - 'sympy>=1.10' + 'sympy-for-algebra>=1.12' ], classifiers=[ 'Development Status :: 4 - Beta', diff --git a/version.py b/version.py index 8286a99..baa4a76 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -__version__ = '0.13.0.dev' \ No newline at end of file +__version__ = '1.0.0rc0' \ No newline at end of file From 31709353284a6e16f470da19f338927316ecc6f9 Mon Sep 17 00:00:00 2001 From: gutow Date: Tue, 2 Jan 2024 16:37:56 -0600 Subject: [PATCH 08/10] to version 1.0.0rc0. doc updates. Preparser fixes. --- Demonstration of equation class.ipynb | 463 +- ReadMe.md | 13 +- algebra_with_sympy/algebraic_equation.py | 38 +- algebra_with_sympy/preparser.py | 9 +- docs/algebra_with_sympy.html | 71 +- .../algebraic_equation.html | 35623 +--------------- docs/algebra_with_sympy/preparser.html | 196 +- docs/algebra_with_sympy/version.html | 10 +- docs/resources/simple_example.png | Bin 57091 -> 53302 bytes docs/search.js | 2 +- setup.py | 2 +- tests/test_algebraic_equation.py | 20 +- tests/test_preparser.py | 3 + 13 files changed, 2664 insertions(+), 33786 deletions(-) diff --git a/Demonstration of equation class.ipynb b/Demonstration of equation class.ipynb index 641a217..96bf07b 100644 --- a/Demonstration of equation class.ipynb +++ b/Demonstration of equation class.ipynb @@ -28,7 +28,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "This notebook is running Algebra_with_Sympy version 0.12.0.\n" + "This notebook is running Algebra_with_Sympy version 1.0.0rc0.\n" ] } ], @@ -52,11 +52,11 @@ "Integration |\n", "Combining Equations | \n", "Output options | \n", - "Control Interpretation of Integers (new in v0.12.0) | \n", + "Control Interpretation of Integers | \n", "Utility operations | \n", "Errors tested for | \n", - "Automatic Solutions (new in v0.11.0) | \n", - "Check version\n", + "Automatic Solutions | \n", + "Check version\n", "### General Examples" ] }, @@ -1763,7 +1763,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Substituting in numbers and units" + "### Substituting in numbers and units\n", + "Algebra_with_Sympy has a convenience operation `units(...)` which takes a string of space \n", + "separated symbols to use as units. This simply declares the symbols \n", + "to be positive, making them behave as units. This does not create units \n", + "that know about conversions, prefixes or systems of units. This lack \n", + "is on purpose to provide units that require the user to worry about \n", + "conversions (ideal in a teaching situation). To get units with built-in \n", + "conversions see `sympy.physics.units`." ] }, { @@ -1786,13 +1793,13 @@ } ], "source": [ - "var('L atm mol K', positive=True, real=True) # Some units\n", + "units('L atm mol K') # Liters, atmospheres, moles and Kelvin\n", "eq2.subs({R:0.08206*L*atm/mol/K,T:273*K,n:1.00*mol,V:24.0*L})" ] }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 75, "metadata": {}, "outputs": [ { @@ -1804,7 +1811,7 @@ "Equation(V, 7.47*L)" ] }, - "execution_count": 74, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -1822,19 +1829,19 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/latex": [ - "$E=Eo - \\frac{R T \\ln{\\left(Q \\right)}}{F z}\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N1})$" + "$E=Eo - \\frac{R T \\log{\\left(Q \\right)}}{F z}\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N1})$" ], "text/plain": [ - "Equation(E, Eo - R*T*ln(Q)/(F*z))" + "Equation(E, Eo - R*T*log(Q)/(F*z))" ] }, - "execution_count": 75, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } @@ -1848,19 +1855,19 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/latex": [ - "$E + \\frac{R T \\ln{\\left(Q \\right)}}{F z}=Eo\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N2})$" + "$E + \\frac{R T \\log{\\left(Q \\right)}}{F z}=Eo\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N2})$" ], "text/plain": [ - "Equation(E + R*T*ln(Q)/(F*z), Eo)" + "Equation(E + R*T*log(Q)/(F*z), Eo)" ] }, - "execution_count": 76, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -1872,19 +1879,19 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/latex": [ - "$R T \\ln{\\left(Q \\right)}=F z \\left(- E + Eo\\right)\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N3})$" + "$R T \\log{\\left(Q \\right)}=F z \\left(- E + Eo\\right)\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N3})$" ], "text/plain": [ - "Equation(R*T*ln(Q), F*z*(-E + Eo))" + "Equation(R*T*log(Q), F*z*(-E + Eo))" ] }, - "execution_count": 77, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -1896,19 +1903,19 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "text/latex": [ - "$\\ln{\\left(Q \\right)}=\\frac{F z \\left(- E + Eo\\right)}{R T}\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N4})$" + "$\\log{\\left(Q \\right)}=\\frac{F z \\left(- E + Eo\\right)}{R T}\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N4})$" ], "text/plain": [ - "Equation(ln(Q), F*z*(-E + Eo)/(R*T))" + "Equation(log(Q), F*z*(-E + Eo)/(R*T))" ] }, - "execution_count": 78, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -1920,7 +1927,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 80, "metadata": {}, "outputs": [ { @@ -1932,7 +1939,7 @@ "Equation(Q, exp(F*z*(-E + Eo)/(R*T)))" ] }, - "execution_count": 79, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -1951,7 +1958,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 81, "metadata": {}, "outputs": [ { @@ -1963,7 +1970,7 @@ "Equation(a*c, b/c**2)" ] }, - "execution_count": 80, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } @@ -1975,7 +1982,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 82, "metadata": {}, "outputs": [ { @@ -1987,7 +1994,7 @@ "Equation(Derivative(a*c, b), c**(-2))" ] }, - "execution_count": 81, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -1999,7 +2006,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 83, "metadata": {}, "outputs": [ { @@ -2011,7 +2018,7 @@ "Equation(a, -2*b/c**3)" ] }, - "execution_count": 82, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -2023,7 +2030,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 84, "metadata": {}, "outputs": [ { @@ -2035,7 +2042,7 @@ "Equation(Derivative(a, c), 6*b/c**4)" ] }, - "execution_count": 83, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } @@ -2046,7 +2053,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 85, "metadata": {}, "outputs": [ { @@ -2058,7 +2065,7 @@ "Equation(Derivative(a*c, b, c), -2/c**3)" ] }, - "execution_count": 84, + "execution_count": 85, "metadata": {}, "output_type": "execute_result" } @@ -2070,7 +2077,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 86, "metadata": {}, "outputs": [ { @@ -2082,7 +2089,7 @@ "Equation(Derivative(a, b), -2/c**3)" ] }, - "execution_count": 85, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } @@ -2094,7 +2101,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 87, "metadata": {}, "outputs": [ { @@ -2106,7 +2113,7 @@ "Equation(Derivative(a*c, b, c), -2/c**3)" ] }, - "execution_count": 86, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -2117,7 +2124,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 88, "metadata": {}, "outputs": [ { @@ -2129,7 +2136,7 @@ "Equation(Derivative(log(a*c), b), 1/b)" ] }, - "execution_count": 87, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } @@ -2140,7 +2147,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 89, "metadata": {}, "outputs": [ { @@ -2152,7 +2159,7 @@ "Equation(0, c**(-2))" ] }, - "execution_count": 88, + "execution_count": 89, "metadata": {}, "output_type": "execute_result" } @@ -2164,7 +2171,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 90, "metadata": {}, "outputs": [ { @@ -2176,7 +2183,7 @@ "Equation(0, c**(-2))" ] }, - "execution_count": 89, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -2187,7 +2194,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 91, "metadata": {}, "outputs": [ { @@ -2199,7 +2206,7 @@ "Equation(Derivative(a, c), b*cos(c))" ] }, - "execution_count": 90, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } @@ -2218,7 +2225,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 92, "metadata": {}, "outputs": [ { @@ -2230,7 +2237,7 @@ "b**2/(2*c**2)" ] }, - "execution_count": 91, + "execution_count": 92, "metadata": {}, "output_type": "execute_result" } @@ -2241,7 +2248,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 93, "metadata": {}, "outputs": [ { @@ -2253,7 +2260,7 @@ "a*b*c" ] }, - "execution_count": 92, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -2264,7 +2271,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 94, "metadata": {}, "outputs": [ { @@ -2276,7 +2283,7 @@ "Equation(Integral(a*c, b), b**2/(2*c**2))" ] }, - "execution_count": 93, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -2288,7 +2295,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 95, "metadata": { "scrolled": true }, @@ -2302,7 +2309,7 @@ "Equation(a*b*c, b**2/(2*c**2))" ] }, - "execution_count": 94, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -2313,7 +2320,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 96, "metadata": {}, "outputs": [ { @@ -2325,7 +2332,7 @@ "Equation(a*b*c, b**2/(2*c**2))" ] }, - "execution_count": 95, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -2336,7 +2343,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 97, "metadata": {}, "outputs": [ { @@ -2348,7 +2355,7 @@ "Equation(a**2*c/2, b**2/(2*c**2))" ] }, - "execution_count": 96, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -2368,7 +2375,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 98, "metadata": {}, "outputs": [ { @@ -2380,7 +2387,7 @@ "Equation(a*c, b/c**2)" ] }, - "execution_count": 97, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -2391,7 +2398,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 99, "metadata": {}, "outputs": [ { @@ -2403,7 +2410,7 @@ "Equation(a, b/c)" ] }, - "execution_count": 98, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -2414,7 +2421,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 100, "metadata": {}, "outputs": [ { @@ -2426,7 +2433,7 @@ "Equation(a*c + a, b/c + b/c**2)" ] }, - "execution_count": 99, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -2437,7 +2444,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 101, "metadata": {}, "outputs": [ { @@ -2449,7 +2456,7 @@ "Equation(c, 1/c)" ] }, - "execution_count": 100, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -2460,7 +2467,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 102, "metadata": {}, "outputs": [ { @@ -2472,7 +2479,7 @@ "Equation(1/c, c)" ] }, - "execution_count": 101, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -2483,7 +2490,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 103, "metadata": {}, "outputs": [ { @@ -2495,7 +2502,7 @@ "Equation(a*(Mod(c, 1)), b*(Mod(1/c, 1))/c)" ] }, - "execution_count": 102, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -2506,7 +2513,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 104, "metadata": {}, "outputs": [ { @@ -2518,7 +2525,7 @@ "Equation(a*(Mod(1, c)), b*(Mod(1, 1/c))/c)" ] }, - "execution_count": 103, + "execution_count": 104, "metadata": {}, "output_type": "execute_result" } @@ -2529,7 +2536,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 105, "metadata": {}, "outputs": [ { @@ -2541,7 +2548,7 @@ "Equation(a**(a*c), (b/c)**(b/c**2))" ] }, - "execution_count": 104, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } @@ -2552,7 +2559,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 106, "metadata": {}, "outputs": [ { @@ -2564,7 +2571,7 @@ "Equation((a*c)**a, (b/c**2)**(b/c))" ] }, - "execution_count": 105, + "execution_count": 106, "metadata": {}, "output_type": "execute_result" } @@ -2575,7 +2582,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 107, "metadata": {}, "outputs": [ { @@ -2587,7 +2594,7 @@ "Equation(V*p_1, R*T_1*n)" ] }, - "execution_count": 106, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } @@ -2601,7 +2608,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 108, "metadata": {}, "outputs": [ { @@ -2613,7 +2620,7 @@ "Equation(p_1/p_2, T_1/T_2)" ] }, - "execution_count": 107, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } @@ -2625,7 +2632,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 109, "metadata": {}, "outputs": [ { @@ -2637,7 +2644,7 @@ "Equation(p_1, T_1*p_2/T_2)" ] }, - "execution_count": 108, + "execution_count": 109, "metadata": {}, "output_type": "execute_result" } @@ -2648,7 +2655,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 110, "metadata": {}, "outputs": [ { @@ -2660,7 +2667,7 @@ "Equation(n, V*p_1/(R*T_1))" ] }, - "execution_count": 109, + "execution_count": 110, "metadata": {}, "output_type": "execute_result" } @@ -2673,7 +2680,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 111, "metadata": {}, "outputs": [ { @@ -2685,7 +2692,7 @@ "Equation(V*p_2, T_2*V*p_1/T_1)" ] }, - "execution_count": 110, + "execution_count": 111, "metadata": {}, "output_type": "execute_result" } @@ -2698,7 +2705,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 112, "metadata": {}, "outputs": [ { @@ -2710,7 +2717,7 @@ "Equation(p_1, T_1*p_2/T_2)" ] }, - "execution_count": 111, + "execution_count": 112, "metadata": {}, "output_type": "execute_result" } @@ -2728,7 +2735,7 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 113, "metadata": {}, "outputs": [ { @@ -2737,7 +2744,7 @@ "True" ] }, - "execution_count": 112, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } @@ -2749,7 +2756,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 114, "metadata": {}, "outputs": [ { @@ -2761,7 +2768,7 @@ "Equation(V*p_1, R*T_1*n)" ] }, - "execution_count": 113, + "execution_count": 114, "metadata": {}, "output_type": "execute_result" } @@ -2774,7 +2781,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 115, "metadata": {}, "outputs": [ { @@ -2786,7 +2793,7 @@ "Equation(V*p_1, R*T_1*n)" ] }, - "execution_count": 114, + "execution_count": 115, "metadata": {}, "output_type": "execute_result" } @@ -2799,7 +2806,7 @@ }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 116, "metadata": {}, "outputs": [ { @@ -2808,7 +2815,7 @@ "False" ] }, - "execution_count": 115, + "execution_count": 116, "metadata": {}, "output_type": "execute_result" } @@ -2820,7 +2827,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 117, "metadata": {}, "outputs": [ { @@ -2839,7 +2846,7 @@ "Equation(V*p_1, R*T_1*n)" ] }, - "execution_count": 116, + "execution_count": 117, "metadata": {}, "output_type": "execute_result" } @@ -2852,7 +2859,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 118, "metadata": {}, "outputs": [ { @@ -2871,7 +2878,7 @@ "exp(I*a)" ] }, - "execution_count": 117, + "execution_count": 118, "metadata": {}, "output_type": "execute_result" } @@ -2883,7 +2890,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 119, "metadata": {}, "outputs": [ { @@ -2895,7 +2902,7 @@ "Equation(V*p_1, R*T_1*n)" ] }, - "execution_count": 118, + "execution_count": 119, "metadata": {}, "output_type": "execute_result" } @@ -2906,6 +2913,56 @@ "IdG1" ] }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$$\\begin{equation}V p_{1}=R T_{1} n\\end{equation}$$" + ], + "text/plain": [ + "Equation(V*p_1, R*T_1*n)" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Display equation wrapped as a Latex equation in `\\begin{equation}...\\end{equation}`\n", + "algwsym_config.output.latex_as_equations = True\n", + "IdG1" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$V p_{1}=R T_{1} n\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{IdG1})$" + ], + "text/plain": [ + "Equation(V*p_1, R*T_1*n)" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Turn it back off\n", + "algwsym_config.output.latex_as_equations = False\n", + "IdG1" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2915,7 +2972,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 122, "metadata": {}, "outputs": [ { @@ -2934,7 +2991,7 @@ "2/3" ] }, - "execution_count": 119, + "execution_count": 122, "metadata": {}, "output_type": "execute_result" } @@ -2947,7 +3004,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 123, "metadata": {}, "outputs": [ { @@ -2959,7 +3016,7 @@ "Equation(x**2 + 3*x/2 + 1, 0)" ] }, - "execution_count": 120, + "execution_count": 123, "metadata": {}, "output_type": "execute_result" } @@ -2972,7 +3029,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 124, "metadata": {}, "outputs": [ { @@ -2984,7 +3041,7 @@ "FiniteSet(Equation(x, -3/4 - sqrt(7)*I/4), Equation(x, -3/4 + sqrt(7)*I/4))" ] }, - "execution_count": 121, + "execution_count": 124, "metadata": {}, "output_type": "execute_result" } @@ -2995,7 +3052,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 125, "metadata": {}, "outputs": [], "source": [ @@ -3005,7 +3062,7 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 126, "metadata": {}, "outputs": [ { @@ -3021,7 +3078,7 @@ "0.6666666666666666" ] }, - "execution_count": 123, + "execution_count": 126, "metadata": {}, "output_type": "execute_result" } @@ -3033,7 +3090,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 127, "metadata": {}, "outputs": [ { @@ -3045,7 +3102,7 @@ "Equation(x**2 + 1.5*x + 1, 0)" ] }, - "execution_count": 124, + "execution_count": 127, "metadata": {}, "output_type": "execute_result" } @@ -3057,7 +3114,7 @@ }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 128, "metadata": {}, "outputs": [ { @@ -3069,7 +3126,7 @@ "FiniteSet(Equation(x, -0.75 - 0.661437827766148*I), Equation(x, -0.75 + 0.661437827766148*I))" ] }, - "execution_count": 125, + "execution_count": 128, "metadata": {}, "output_type": "execute_result" } @@ -3080,7 +3137,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 129, "metadata": {}, "outputs": [], "source": [ @@ -3090,7 +3147,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 130, "metadata": {}, "outputs": [ { @@ -3109,7 +3166,7 @@ "2/3" ] }, - "execution_count": 127, + "execution_count": 130, "metadata": {}, "output_type": "execute_result" } @@ -3128,7 +3185,7 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 131, "metadata": {}, "outputs": [ { @@ -3140,7 +3197,7 @@ "Equation(a, b/c)" ] }, - "execution_count": 128, + "execution_count": 131, "metadata": {}, "output_type": "execute_result" } @@ -3151,7 +3208,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 132, "metadata": {}, "outputs": [ { @@ -3163,7 +3220,7 @@ "Equation(b/c, a)" ] }, - "execution_count": 129, + "execution_count": 132, "metadata": {}, "output_type": "execute_result" } @@ -3174,7 +3231,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 133, "metadata": {}, "outputs": [ { @@ -3186,7 +3243,7 @@ "Equation(b/c, a)" ] }, - "execution_count": 130, + "execution_count": 133, "metadata": {}, "output_type": "execute_result" } @@ -3197,7 +3254,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 134, "metadata": {}, "outputs": [ { @@ -3209,7 +3266,7 @@ "a" ] }, - "execution_count": 131, + "execution_count": 134, "metadata": {}, "output_type": "execute_result" } @@ -3220,7 +3277,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 135, "metadata": {}, "outputs": [ { @@ -3232,7 +3289,7 @@ "b/c" ] }, - "execution_count": 132, + "execution_count": 135, "metadata": {}, "output_type": "execute_result" } @@ -3243,7 +3300,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 136, "metadata": {}, "outputs": [ { @@ -3255,7 +3312,7 @@ "Eq(a, b/c)" ] }, - "execution_count": 133, + "execution_count": 136, "metadata": {}, "output_type": "execute_result" } @@ -3266,7 +3323,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 137, "metadata": {}, "outputs": [ { @@ -3278,7 +3335,7 @@ "True" ] }, - "execution_count": 134, + "execution_count": 137, "metadata": {}, "output_type": "execute_result" } @@ -3289,7 +3346,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 138, "metadata": {}, "outputs": [ { @@ -3301,7 +3358,7 @@ "Eq(a, b/c)" ] }, - "execution_count": 135, + "execution_count": 138, "metadata": {}, "output_type": "execute_result" } @@ -3313,7 +3370,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 139, "metadata": {}, "outputs": [ { @@ -3325,7 +3382,7 @@ "False" ] }, - "execution_count": 136, + "execution_count": 139, "metadata": {}, "output_type": "execute_result" } @@ -3336,7 +3393,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 140, "metadata": {}, "outputs": [ { @@ -3348,7 +3405,7 @@ "Equation(a - b/c, 0)" ] }, - "execution_count": 137, + "execution_count": 140, "metadata": {}, "output_type": "execute_result" } @@ -3360,7 +3417,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 141, "metadata": {}, "outputs": [ { @@ -3372,7 +3429,7 @@ "Equation(0, -a + b/c)" ] }, - "execution_count": 138, + "execution_count": 141, "metadata": {}, "output_type": "execute_result" } @@ -3383,7 +3440,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 142, "metadata": {}, "outputs": [ { @@ -3395,7 +3452,7 @@ "Equation(a - b/c, 0)" ] }, - "execution_count": 139, + "execution_count": 142, "metadata": {}, "output_type": "execute_result" } @@ -3413,14 +3470,14 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 143, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "The argument 'a = b/c (t)' is not comparable.\n", + "The argument 'a = b/c' is not comparable.\n", "----\n", "You must specify `side=\"lhs\"` or `side=\"rhs\"` when integrating an Equation\n", "----\n", @@ -3442,7 +3499,7 @@ "True" ] }, - "execution_count": 140, + "execution_count": 143, "metadata": {}, "output_type": "execute_result" } @@ -3482,7 +3539,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 144, "metadata": {}, "outputs": [ { @@ -3494,7 +3551,7 @@ "True" ] }, - "execution_count": 141, + "execution_count": 144, "metadata": {}, "output_type": "execute_result" } @@ -3505,7 +3562,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 145, "metadata": {}, "outputs": [ { @@ -3517,7 +3574,7 @@ "False" ] }, - "execution_count": 142, + "execution_count": 145, "metadata": {}, "output_type": "execute_result" } @@ -3528,7 +3585,7 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 146, "metadata": { "scrolled": true }, @@ -3542,7 +3599,7 @@ "False" ] }, - "execution_count": 143, + "execution_count": 146, "metadata": {}, "output_type": "execute_result" } @@ -3553,7 +3610,7 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 147, "metadata": {}, "outputs": [ { @@ -3565,7 +3622,7 @@ "True" ] }, - "execution_count": 144, + "execution_count": 147, "metadata": {}, "output_type": "execute_result" } @@ -3576,7 +3633,7 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 148, "metadata": {}, "outputs": [ { @@ -3588,7 +3645,7 @@ "True" ] }, - "execution_count": 145, + "execution_count": 148, "metadata": {}, "output_type": "execute_result" } @@ -3599,7 +3656,7 @@ }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 149, "metadata": {}, "outputs": [ { @@ -3611,7 +3668,7 @@ "Equation(-1, -1)" ] }, - "execution_count": 146, + "execution_count": 149, "metadata": {}, "output_type": "execute_result" } @@ -3622,7 +3679,7 @@ }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 150, "metadata": {}, "outputs": [ { @@ -3634,7 +3691,7 @@ "True" ] }, - "execution_count": 147, + "execution_count": 150, "metadata": {}, "output_type": "execute_result" } @@ -3653,7 +3710,7 @@ }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 151, "metadata": {}, "outputs": [ { @@ -3665,7 +3722,7 @@ "Equation(a - b, c/a)" ] }, - "execution_count": 148, + "execution_count": 151, "metadata": {}, "output_type": "execute_result" } @@ -3677,7 +3734,7 @@ }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 152, "metadata": {}, "outputs": [ { @@ -3689,7 +3746,7 @@ "FiniteSet(Equation(a, b/2 - sqrt(b**2 + 4*c)/2), Equation(a, b/2 + sqrt(b**2 + 4*c)/2))" ] }, - "execution_count": 149, + "execution_count": 152, "metadata": {}, "output_type": "execute_result" } @@ -3700,7 +3757,7 @@ }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 153, "metadata": {}, "outputs": [ { @@ -3712,7 +3769,7 @@ "FiniteSet({a: b/2 - sqrt(b**2 + 4*c)/2}, {a: b/2 + sqrt(b**2 + 4*c)/2})" ] }, - "execution_count": 150, + "execution_count": 153, "metadata": {}, "output_type": "execute_result" } @@ -3723,7 +3780,7 @@ }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 154, "metadata": {}, "outputs": [ { @@ -3735,7 +3792,7 @@ "FiniteSet(Equation(c, a**2 - a*b))" ] }, - "execution_count": 151, + "execution_count": 154, "metadata": {}, "output_type": "execute_result" } @@ -3746,7 +3803,7 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 155, "metadata": {}, "outputs": [ { @@ -3758,7 +3815,7 @@ "FiniteSet(Equation(b, (a**2 - c)/a))" ] }, - "execution_count": 152, + "execution_count": 155, "metadata": {}, "output_type": "execute_result" } @@ -3769,19 +3826,19 @@ }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 156, "metadata": {}, "outputs": [ { "data": { "text/latex": [ - "$\\left\\{Q=e^{\\frac{F z \\left(- E + Eo\\right)}{R T}}\\,\\,\\,\\,\\,\\,\\,\\,\\,\\,(\\text{N5})\\right\\}$" + "$\\left\\{Q=e^{\\frac{F z \\left(- E + Eo\\right)}{R T}}\\right\\}$" ], "text/plain": [ "FiniteSet(Equation(Q, exp(F*z*(-E + Eo)/(R*T))))" ] }, - "execution_count": 153, + "execution_count": 156, "metadata": {}, "output_type": "execute_result" } @@ -3792,7 +3849,7 @@ }, { "cell_type": "code", - "execution_count": 154, + "execution_count": 157, "metadata": {}, "outputs": [ { @@ -3804,7 +3861,7 @@ "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)))" ] }, - "execution_count": 154, + "execution_count": 157, "metadata": {}, "output_type": "execute_result" } @@ -3819,7 +3876,7 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": 158, "metadata": {}, "outputs": [ { @@ -3831,7 +3888,7 @@ "FiniteSet(3/2 - y/2, -y/2 - 3/2)" ] }, - "execution_count": 155, + "execution_count": 158, "metadata": {}, "output_type": "execute_result" } @@ -3851,7 +3908,7 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": 159, "metadata": {}, "outputs": [ { @@ -3870,7 +3927,7 @@ "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)))" ] }, - "execution_count": 156, + "execution_count": 159, "metadata": {}, "output_type": "execute_result" } @@ -3882,7 +3939,7 @@ }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 160, "metadata": {}, "outputs": [ { @@ -3894,7 +3951,7 @@ "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)))" ] }, - "execution_count": 157, + "execution_count": 160, "metadata": {}, "output_type": "execute_result" } @@ -3906,7 +3963,7 @@ }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 161, "metadata": {}, "outputs": [ { @@ -3925,7 +3982,7 @@ "FiniteSet(Equation(a, b/2 - sqrt(b**2 + 4*c)/2), Equation(a, b/2 + sqrt(b**2 + 4*c)/2))" ] }, - "execution_count": 158, + "execution_count": 161, "metadata": {}, "output_type": "execute_result" } @@ -3938,7 +3995,7 @@ }, { "cell_type": "code", - "execution_count": 159, + "execution_count": 162, "metadata": {}, "outputs": [ { @@ -3957,7 +4014,7 @@ "FiniteSet({a: b/2 - sqrt(b**2 + 4*c)/2}, {a: b/2 + sqrt(b**2 + 4*c)/2})" ] }, - "execution_count": 159, + "execution_count": 162, "metadata": {}, "output_type": "execute_result" } @@ -3968,7 +4025,7 @@ }, { "cell_type": "code", - "execution_count": 160, + "execution_count": 163, "metadata": {}, "outputs": [ { @@ -3987,7 +4044,7 @@ "Equation(a, b/2 - sqrt(b**2 + 4*c)/2)" ] }, - "execution_count": 160, + "execution_count": 163, "metadata": {}, "output_type": "execute_result" } @@ -3998,7 +4055,7 @@ }, { "cell_type": "code", - "execution_count": 161, + "execution_count": 164, "metadata": {}, "outputs": [ { @@ -4010,7 +4067,7 @@ "Equation(a, b/2 + sqrt(b**2 + 4*c)/2)" ] }, - "execution_count": 161, + "execution_count": 164, "metadata": {}, "output_type": "execute_result" } @@ -4030,7 +4087,7 @@ }, { "cell_type": "code", - "execution_count": 162, + "execution_count": 165, "metadata": {}, "outputs": [ { @@ -4039,7 +4096,7 @@ "[Equation(a, b/2 - sqrt(b**2 + 4*c)/2), Equation(a, b/2 + sqrt(b**2 + 4*c)/2)]" ] }, - "execution_count": 162, + "execution_count": 165, "metadata": {}, "output_type": "execute_result" } @@ -4051,7 +4108,7 @@ }, { "cell_type": "code", - "execution_count": 163, + "execution_count": 166, "metadata": {}, "outputs": [ { @@ -4060,7 +4117,7 @@ "[{a: b/2 - sqrt(b**2 + 4*c)/2}, {a: b/2 + sqrt(b**2 + 4*c)/2}]" ] }, - "execution_count": 163, + "execution_count": 166, "metadata": {}, "output_type": "execute_result" } @@ -4071,7 +4128,7 @@ }, { "cell_type": "code", - "execution_count": 164, + "execution_count": 167, "metadata": {}, "outputs": [ { @@ -4083,7 +4140,7 @@ " [Equation(x, 3), Equation(y, -3)]]" ] }, - "execution_count": 164, + "execution_count": 167, "metadata": {}, "output_type": "execute_result" } @@ -4101,16 +4158,16 @@ }, { "cell_type": "code", - "execution_count": 165, + "execution_count": 168, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'0.12.0'" + "'1.0.0rc0'" ] }, - "execution_count": 165, + "execution_count": 168, "metadata": {}, "output_type": "execute_result" } @@ -4121,16 +4178,16 @@ }, { "cell_type": "code", - "execution_count": 166, + "execution_count": 169, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'0.12.0'" + "'1.0.0rc0'" ] }, - "execution_count": 166, + "execution_count": 169, "metadata": {}, "output_type": "execute_result" } @@ -4157,7 +4214,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.6" + "version": "3.10.12" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/ReadMe.md b/ReadMe.md index 06ff550..093d0d3 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,7 +39,7 @@ notebook is shown immediately below: The last cell illustrates how it is possible to substitute numbers with units into the solved equation to calculate a numerical solution with -proper units. +proper units. The `units(...)` operation is part this package, not Sympy. In IPython environments (IPython and Jupyter) there is also a shorthand syntax for entering equations provided through the IPython preparser. An @@ -108,7 +108,7 @@ $\frac{a}{b} = \frac{c}{d}$ or $e^{\frac{-x^2}{\sigma^2}}$. To also see the * **Automatic wrapping of `Equations` as Latex equations** can be activated by setting `algwsym_config.output.latex_as_equations` to `True`. The default is `False`. Setting this to `True` wraps output as LaTex equations, - wrapping them in \begin{equation}...\end{equation}. Equations formatted + wrapping them in `\begin{equation}...\end{equation}`. Equations formatted this way will **not** be labeled with the internal name for the equation, independent of the setting of `algwsym_config.output.label`. @@ -145,11 +145,20 @@ github](https://github.com/gutow/Algebra_with_Sympy/issues). ## Change Log * 1.0.0rc0 + * Added convenience operation `units(...)` which takes a string of space + separated symbols to use as units. This simply declares the symbols + to be positive, making them behave as units. This does not create units + that know about conversions, prefixes or systems of units. This lack + is on purpose to provide units that require the user to worry about + conversions (ideal in a teaching situation). To get units with built-in + conversions see `sympy.physics.units`. * Fixed issue #23 where `cos()` multiplied by a factor was not the same type of object after `simplify()` acted on an expression. Required embedding the `Equation` type in the sympy library. Until `Equation` is incorporated into the primary Sympy repository a customized version of the latest stable release will be used. + * Fixed issue where trailing comments (ie. `# a comment` at the end of a + line) lead to input errors using compact `=@` notation. * `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}`. Equations formatted this way diff --git a/algebra_with_sympy/algebraic_equation.py b/algebra_with_sympy/algebraic_equation.py index 6fcdc39..fcbe86c 100644 --- a/algebra_with_sympy/algebraic_equation.py +++ b/algebra_with_sympy/algebraic_equation.py @@ -305,7 +305,7 @@ def __init__(self): 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{ + output as LaTex equations wrapping them in `\\begin{equation}...\\end{ equation}`. """ pass @@ -358,7 +358,7 @@ def latex_as_equations(self): the output will be ``` $$ - \\begin{equation}\\frac{a}{b}=c\end{equation} + \\begin{equation}\\frac{a}{b}=c\\end{equation} $$ ``` """ @@ -516,6 +516,40 @@ def unset_integers_as_exact(): __command_line_printing__) # print("For type Equation overriding plain text formatter = " + str(old)) +def units(names): + """ + This operation declares the symbols to be positive values, so that sympy will handle them properly + when simplifying expressions containing units. + + :param string names: a string containing a space separated list of symbols to be treated as units. + + :return string list of defined units: calls `name = symbols(name, + positive=True)` in the interactive namespace for each symbol name. + """ + from sympy.core.symbol import symbols + #import __main__ as shell + from IPython import get_ipython + syms = names.split(' ') + user_namespace = None + retstr = '' + if get_ipython(): + user_namespace = get_ipython().user_ns + else: + import sys + frame_num = 0 + frame_name = None + while frame_name != '__main__' and frame_num < 50: + user_namespace = sys._getframe(frame_num).f_globals + frame_num +=1 + frame_name = user_namespace['__name__'] + retstr +='(' + for k in syms: + user_namespace[k] = symbols(k, positive = True) + retstr += k + ',' + retstr = retstr[:-1] + ')' + return retstr + + def solve(f, *symbols, **flags): """ Override of sympy `solve()`. diff --git a/algebra_with_sympy/preparser.py b/algebra_with_sympy/preparser.py index 6e3f676..db73faa 100644 --- a/algebra_with_sympy/preparser.py +++ b/algebra_with_sympy/preparser.py @@ -28,7 +28,14 @@ def algebra_with_sympy_preparser(lines): lines = [lines] for k in lines: if '=@' in k: - linesplit = k.split('=@') + drop_comments = k.split('#') + to_rephrase = '' + if len(drop_comments) > 2: + for i in range(len(drop_comments)-1): + to_rephrase += drop_comments[i] + else: + to_rephrase = drop_comments[0] + linesplit = to_rephrase.split('=@') eqsplit = linesplit[1].split('=') if len(eqsplit)!=2: raise ValueError('The two sides of the equation must be' \ diff --git a/docs/algebra_with_sympy.html b/docs/algebra_with_sympy.html index cc606e2..0750efb 100644 --- a/docs/algebra_with_sympy.html +++ b/docs/algebra_with_sympy.html @@ -3,14 +3,14 @@ - + algebra_with_sympy API documentation - - + +