Reputation:
I recently started using Jupyter notebook for writing my homework assignments for a Fluid Dynamics class. I use an IPython kernel with Sympy for symbolic computations. Often, I need to print the result of my symbolic calculations as LaTeX output. As an example, see the following code
import sympy as s
s.init_printing()
from IPython.display import display
r,t = s.symbols('r,theta')
u_x = r*s.sin(t)
display(u_x)
This prints the LaTeX rendering of $r\sin\theta$
in the cell below the code.
Is there someway I could add
$u_x = $
in front of the output? In this example, the desired output would be
My purpose is that the output of my display(u_x)
statement in Jupyter notebook should be like a regular equation in a LaTeX document. The reader should not have to read the code part to understand what quantity that I am displaying in the equation.
Upvotes: 2
Views: 2535
Reputation: 305
I've been puttering with a somewhat different approach that generates this kind of output directly from assignment statements. I call this display math operation
or dmo
for short. You can make calls such as dmo(p=a*b/c**2)
and get the typeset output p=...
displayed in Jupyter notebooks.
Along with this I am also working on how to extend sympy functions more generally to show what operations are done. I have working examples for differentiation and integration. I hope to have time to look into how to embed this in sympy, rather than bolt it on.
You can try the examples in mybinder: The git repository is https://github.com/gutow/Easy_Pretty_Math. Please put suggestions and issues in the git repository issues.
Upvotes: 0
Reputation: 4914
I just came up with one (admittedly quite ugly) way to do it, by creating a custom class:
import sympy as sp
from sympy.printing.pretty.stringpict import prettyForm
class NamedExpression(sp.Expr):
def __init__(self, name, expr=None, **kwargs):
self._symbol = sp.Symbol(name, **kwargs)
self._expr = expr
def assign(self, expr):
self._expr = expr
def as_symbol(self):
return self._symbol
def as_eq(self):
if self._expr is None:
raise RuntimeError('No expression available')
return sp.Eq(self._symbol, self._expr)
def _print_myself(self, printer):
return _print_function(self, printer)
_sympystr = _sympyrepr = _latex = _print_myself
def _pretty(self, printer):
return prettyForm(_print_function(self, printer))
def __getattribute__(self, name):
if name in ['_symbol', '_expr', 'assign', 'as_symbol', 'as_eq',
'_pretty', '_sympystr', '_sympyrepr', '_latex']:
return super().__getattribute__(name)
expr = self._expr
if expr is not None:
return expr.__getattribute__(name)
return self._symbol.__getattribute__(name)
def _print_function(obj, printer):
expr = obj._expr
what = obj._symbol if expr is None else expr
return printer.doprint(what)
With this class, you can create your symbol like this:
u_x = NamedExpression('u_x')
You can use this in SymPy expressions and it will be displayed like a normal symbol.
At some point, you can assign something to this symbol:
u_x.assign(r * sp.sin(t))
After that, you can again use it in SymPy expressions and it will behave like the assigned expression.
If you want to display the definition, just do
u_x.as_eq()
Upvotes: 2
Reputation: 4914
I've created an arguably slightly less ugly variation of my previous answer, which avoids all the complicated printing stuff. However, I'm losing the ability to assign an expression at a later time; the expression has to be specified at construction time. But that's probably not a big deal.
Instead of defining a single class, I'm defining a custom class on demand:
import sympy as sp
def named_expression(symbol, expr):
class NamedExpression(type(expr)):
def __new__(cls, sym, ex):
# NB: we don't call the base class' __new__()!
self = object.__new__(cls)
if isinstance(sym, sp.Symbol):
self._symbol = sym
else:
self._symbol = sp.Symbol(sym)
self._expr = ex
return self
def __getattribute__(self, name):
if name in ['_symbol', '_expr', 'as_symbol', 'as_eq']:
return super().__getattribute__(name)
return self._expr.__getattribute__(name)
def as_symbol(self):
return self._symbol
def as_eq(self):
return sp.Eq(self._symbol, self._expr)
return NamedExpression(symbol, expr)
It can be used like this:
u_x = named_expression('u_x', r * sp.sin(t))
Or, if you already have a symbol named u_x
, you can overwrite it like this:
u_x = named_expression(u_x, r * sp.sin(t))
As before, the equality can be shown like this:
u_x.as_eq()
Note that this is not at all specific to IPython, it should work wherever SymPy is available.
Upvotes: 1
Reputation:
After scanning through IPython's display
module some more, it turns out I can use a function like below
from sympy import latex
from IPython.display import display_latex
def disp(idx, symObj):
eqn = '\\[' + idx + ' = ' + latex(symObj) + '\\]'
display_latex(eqn,raw=True)
return
Now, disp('u_x',u_x)
gives the desired output.
Upvotes: 3
Reputation: 7293
Add
s.init_session()
right after the import of sympy.
Then, simply putting
u_x
in a cell will render properly, without the need to call display.
Upvotes: 0