如何在 Jupyter Notebook 中为新结构扩展 SymPy 漂亮打印?
How do I extend SymPy pretty printing for new structures in Jupyter notebook?
注意: 这是 this question in Math Stack Exchange. I had to first pose the question in Math StackExchange because Whosebug doesn't have MathJax 的副本。但是,几乎所有 SymPy 问题都在 Whosebug 上。有关所需输出的排版,请参阅 Math Stack Exchange 版本。 Math StackExchange 上的一位编辑建议我在这里交叉 post 它。
在 Jupyter Notebook 中,如果我们执行此代码:
import sympy as sp
sp.init_printing()
x,y=sp.symbols('x,y')
x**2+sp.sin(y)
由于SymPy's pretty printing process,我们将得到一个不错的输出,无需进一步编码,看起来像
现在假设我们这样做:
class MetricSpace:
def __init__(self, M, d):
self.M = M
self.d = d
def __repr__(self):
return f"$({self.M}, {self.d})$ {self.__class__.__name__}"
Re,d=sp.symbols(r'\Re,d')
MetricSpace(Re,d)
那么我们得到的输出是
如果我们这样做
from IPython.core.display import Markdown
Markdown(repr(MetricSpace(Re,d)))
然后我得到了想要的输出,看起来像
我们如何编写上述代码,以便 SymPy 的漂亮打印机在 Jupyter notebook 中提供所需的输出,而无需将其包装在 Markdown(repr(...))
中?
这是一个可以正常运行的片段。它可能无法完成所有事情
你还想要,但希望它能让你开始。
import sympy as sp
sp.init_printing()
class MetricSpace(sp.Expr):
def __init__(self, M, d):
self.M = M
self.d = d
def _latex(self, printer=None):
return f"({printer.doprint(self.M)}, {printer.doprint(self.d)})\ \text{{{self.__class__.__name__}}}"
Re, d = sp.symbols(r'\Re,d')
MetricSpace(Re, d)
有关更多信息,请参阅 here
有关 Sympy 打印系统的信息以及如何连接到它。关键的事情
需要注意的是,如果你想让你的对象进入,你应该子类化 Expr
Sympy 漂亮打印的域,乳胶打印机调用
_latex
获取其 LaTeX 的方法。它不需要用美元符号包裹,
你应该使用 printer.doprint
来获取嵌套表达式的 LaTeX。
这是在我的笔记本上生成的屏幕截图:
PS:我对一个指标 space 感兴趣,其基础集用“\Re
”表示。如果
你是说实数,我可以建议使用 sp.Reals
?
注:上面Izaak van Dongen的回答我已经接受了。这个答案是为那些可能感兴趣的人提供问题的背景。
我在读一本关于 SDE 的书,我问了 a question on the measure theory presentation of probability space. There are a large number of structural definitions involved. I wanted to use SymPy to organize them and present the type signatures in something mimicking Axiom style。
为了在 Python 3 和 SymPy 中愉快地做到这一点,我需要一些东西:
- 一种动态执行的方式function signatures。
- 一种pretty-printing复杂代数类型签名的方法(本题)。
我开始实施这些定义。为了检查它们是否组织正确,我问了
一个关于 extending the concept of distribution function to any ordered space 的问题,在形式化概率 space 的定义时自然会出现。在 SymPy 形式化上下文之外,我认为没有人知道为什么这个问题很自然或有趣。
一道题confirming that my stack of definitions for probability space was correct(没有引入SymPy)
有了上面的 pretty-printing 解决方案,以下几个定义给出了我的解决方案的样式(没有引用大约 85 个定义的整个内容):
import sympy as sp # I am at version 1.6.1
from typen import strict_type_hints, enforce_type_hints
from traits.api import Array, Either, Enum, Instance, Int, Str, Tuple
class Concept(sp.Expr):
def __init__(self, name, value):
self.name = name
self.value = value
def _latex(self, printer=None):
return f"{self.name}:\ \text{{{self.__class__.__name__}}}"
class NonemptySet(Concept):
def __init__(self, name, value):
if value==sp.S.EmptySet:
raise ValueError("Set must not be empty")
super().__init__(name, value)
def _latex(self, printer=None):
return self.name
class Reals(NonemptySet):
@strict_type_hints
def __init__(self):
self.name = sp.symbols('\Re')
super().__init__(self.name,sp.Reals)
def _latex(self, printer=None):
return self.name
class SampleSpace (NonemptySet):
pass
class Algebra(NonemptySet):
@strict_type_hints
def __init__(self,
name: sp.Symbol,
Ω: SampleSpace,
A: Either(NonemptySet, sp.Symbol)):
self.Ω=Ω
super().__init__(name, A)
def _latex(self, printer=None):
math=str(self.name).replace('$','')
math2 = self.Ω._latex(printer)
return f"{math}:\ \text{{{self.__class__.__name__} on }} ({math2})"
class Algebra(Algebra):
@strict_type_hints
def __init__(self, name: sp.Symbol, Ω: SampleSpace, A: Algebra):
self.Ω=Ω
super().__init__(name, Ω, A)
class EventSpace(Algebra):
@strict_type_hints
def __init__(self, name: sp.Symbol, Ω: SampleSpace, A: Algebra):
super().__init__(name, Ω, A)
class AdditiveFunction(Concept):
@strict_type_hints
def __init__(self,
name: sp.core.function.UndefinedFunction,
Ω: SampleSpace,
A: Algebra,
f: sp.core.function.UndefinedFunction):
self.Ω = Ω
self.A = A
super().__init__(name, f)
def _latex(self, printer=None):
math2 = self.A._latex(printer)
return f"{self.name}: {self.A.name} \to \Re \ \text{{{self.__class__.__name__} on }} {math2}"
等等。任何关于改进上述草图的更“SymPy-thonic”方式的评论或建议将不胜感激。
如果您只关心与 Jupyter 笔记本的集成,那么您需要 IPython _repr_latex_
挂钩:
class MetricSpace:
def _repr_latex_(self):
# this is text-mode latex, so needs $ to enter math mode
return f"$({self.M}, {self.d})$ {self.__class__.__name__}"
这将使 MetricSpace(...)
在 jupter notebook 中显示为 latex,并且完全独立于 sympy。
另外,sympy
有一个 latex
函数。如果你想支持它,你需要实现 _latex
:
class MetricSpace:
def _latex(self, printer):
# this is math-mode latex, so needs \text to enter text mode
# if the class members support this hook, you can use `printer._print` to recurse
return f"{printer._print(self.M)}, {printer._print(self.d)}$ \text{{{self.__class__.__name__}}}"
这将使 sympy.latex(MetricSpace(...))
工作。
最后,在调用 init_printing(use_latex='mathjax')
之后,还有将这两种模式连接在一起的 sympy 集成。这在 Sympy 1.6 和 1.7 之间发生了变化。
- 在 Sympy 1.6 中,提供了一个 ipython latex 格式化程序
- class
sympy.Basic
(和其他一些)
list
s、set
s 等这些对象
- 在 Sympy 1.7 中,提供了一个 ipython latex 格式化程序
- 子class属于
sympy.printing.defaults.Printable
list
s、set
s 等任何实现 _latex
或 subclasses Printable
的对象
Subclassing Expr
是一个坏主意,因为这会向您的 class 添加数十个可能毫无意义的方法和运算符重载。 Subclassing Basic
没有那么糟糕,但是如果你的 class 不打算在 sympy
中使用,那么它会遇到相同问题的较小版本
注意: 这是 this question in Math Stack Exchange. I had to first pose the question in Math StackExchange because Whosebug doesn't have MathJax 的副本。但是,几乎所有 SymPy 问题都在 Whosebug 上。有关所需输出的排版,请参阅 Math Stack Exchange 版本。 Math StackExchange 上的一位编辑建议我在这里交叉 post 它。
在 Jupyter Notebook 中,如果我们执行此代码:
import sympy as sp
sp.init_printing()
x,y=sp.symbols('x,y')
x**2+sp.sin(y)
由于SymPy's pretty printing process,我们将得到一个不错的输出,无需进一步编码,看起来像
现在假设我们这样做:
class MetricSpace:
def __init__(self, M, d):
self.M = M
self.d = d
def __repr__(self):
return f"$({self.M}, {self.d})$ {self.__class__.__name__}"
Re,d=sp.symbols(r'\Re,d')
MetricSpace(Re,d)
那么我们得到的输出是
如果我们这样做
from IPython.core.display import Markdown
Markdown(repr(MetricSpace(Re,d)))
然后我得到了想要的输出,看起来像
我们如何编写上述代码,以便 SymPy 的漂亮打印机在 Jupyter notebook 中提供所需的输出,而无需将其包装在 Markdown(repr(...))
中?
这是一个可以正常运行的片段。它可能无法完成所有事情 你还想要,但希望它能让你开始。
import sympy as sp
sp.init_printing()
class MetricSpace(sp.Expr):
def __init__(self, M, d):
self.M = M
self.d = d
def _latex(self, printer=None):
return f"({printer.doprint(self.M)}, {printer.doprint(self.d)})\ \text{{{self.__class__.__name__}}}"
Re, d = sp.symbols(r'\Re,d')
MetricSpace(Re, d)
有关更多信息,请参阅 here
有关 Sympy 打印系统的信息以及如何连接到它。关键的事情
需要注意的是,如果你想让你的对象进入,你应该子类化 Expr
Sympy 漂亮打印的域,乳胶打印机调用
_latex
获取其 LaTeX 的方法。它不需要用美元符号包裹,
你应该使用 printer.doprint
来获取嵌套表达式的 LaTeX。
这是在我的笔记本上生成的屏幕截图:
PS:我对一个指标 space 感兴趣,其基础集用“\Re
”表示。如果
你是说实数,我可以建议使用 sp.Reals
?
注:上面Izaak van Dongen的回答我已经接受了。这个答案是为那些可能感兴趣的人提供问题的背景。
我在读一本关于 SDE 的书,我问了 a question on the measure theory presentation of probability space. There are a large number of structural definitions involved. I wanted to use SymPy to organize them and present the type signatures in something mimicking Axiom style。
为了在 Python 3 和 SymPy 中愉快地做到这一点,我需要一些东西:
- 一种动态执行的方式function signatures。
- 一种pretty-printing复杂代数类型签名的方法(本题)。
我开始实施这些定义。为了检查它们是否组织正确,我问了
一个关于 extending the concept of distribution function to any ordered space 的问题,在形式化概率 space 的定义时自然会出现。在 SymPy 形式化上下文之外,我认为没有人知道为什么这个问题很自然或有趣。
一道题confirming that my stack of definitions for probability space was correct(没有引入SymPy)
有了上面的 pretty-printing 解决方案,以下几个定义给出了我的解决方案的样式(没有引用大约 85 个定义的整个内容):
import sympy as sp # I am at version 1.6.1
from typen import strict_type_hints, enforce_type_hints
from traits.api import Array, Either, Enum, Instance, Int, Str, Tuple
class Concept(sp.Expr):
def __init__(self, name, value):
self.name = name
self.value = value
def _latex(self, printer=None):
return f"{self.name}:\ \text{{{self.__class__.__name__}}}"
class NonemptySet(Concept):
def __init__(self, name, value):
if value==sp.S.EmptySet:
raise ValueError("Set must not be empty")
super().__init__(name, value)
def _latex(self, printer=None):
return self.name
class Reals(NonemptySet):
@strict_type_hints
def __init__(self):
self.name = sp.symbols('\Re')
super().__init__(self.name,sp.Reals)
def _latex(self, printer=None):
return self.name
class SampleSpace (NonemptySet):
pass
class Algebra(NonemptySet):
@strict_type_hints
def __init__(self,
name: sp.Symbol,
Ω: SampleSpace,
A: Either(NonemptySet, sp.Symbol)):
self.Ω=Ω
super().__init__(name, A)
def _latex(self, printer=None):
math=str(self.name).replace('$','')
math2 = self.Ω._latex(printer)
return f"{math}:\ \text{{{self.__class__.__name__} on }} ({math2})"
class Algebra(Algebra):
@strict_type_hints
def __init__(self, name: sp.Symbol, Ω: SampleSpace, A: Algebra):
self.Ω=Ω
super().__init__(name, Ω, A)
class EventSpace(Algebra):
@strict_type_hints
def __init__(self, name: sp.Symbol, Ω: SampleSpace, A: Algebra):
super().__init__(name, Ω, A)
class AdditiveFunction(Concept):
@strict_type_hints
def __init__(self,
name: sp.core.function.UndefinedFunction,
Ω: SampleSpace,
A: Algebra,
f: sp.core.function.UndefinedFunction):
self.Ω = Ω
self.A = A
super().__init__(name, f)
def _latex(self, printer=None):
math2 = self.A._latex(printer)
return f"{self.name}: {self.A.name} \to \Re \ \text{{{self.__class__.__name__} on }} {math2}"
等等。任何关于改进上述草图的更“SymPy-thonic”方式的评论或建议将不胜感激。
如果您只关心与 Jupyter 笔记本的集成,那么您需要 IPython _repr_latex_
挂钩:
class MetricSpace:
def _repr_latex_(self):
# this is text-mode latex, so needs $ to enter math mode
return f"$({self.M}, {self.d})$ {self.__class__.__name__}"
这将使 MetricSpace(...)
在 jupter notebook 中显示为 latex,并且完全独立于 sympy。
另外,sympy
有一个 latex
函数。如果你想支持它,你需要实现 _latex
:
class MetricSpace:
def _latex(self, printer):
# this is math-mode latex, so needs \text to enter text mode
# if the class members support this hook, you can use `printer._print` to recurse
return f"{printer._print(self.M)}, {printer._print(self.d)}$ \text{{{self.__class__.__name__}}}"
这将使 sympy.latex(MetricSpace(...))
工作。
最后,在调用 init_printing(use_latex='mathjax')
之后,还有将这两种模式连接在一起的 sympy 集成。这在 Sympy 1.6 和 1.7 之间发生了变化。
- 在 Sympy 1.6 中,提供了一个 ipython latex 格式化程序
- class
sympy.Basic
(和其他一些) list
s、set
s 等这些对象
- class
- 在 Sympy 1.7 中,提供了一个 ipython latex 格式化程序
- 子class属于
sympy.printing.defaults.Printable
list
s、set
s 等任何实现_latex
或 subclassesPrintable
的对象
- 子class属于
Subclassing Expr
是一个坏主意,因为这会向您的 class 添加数十个可能毫无意义的方法和运算符重载。 Subclassing Basic
没有那么糟糕,但是如果你的 class 不打算在 sympy