pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- pythonfan の投稿
返信数: 8
「フォーラム内のブログ」と同じものを、外部(忍者ブログ)にも作りましたので、
再度同じ投稿をさせていただきます。

初めて投稿させていただきます。
高校で数学の教員(現在は講師)をしています。
こんなものがあったら便利、と思って作ってみました。
最終的にできたのは、texの数式コードを書き込めば、答え付きの演習プリントができる、というもの。

python: 軽量プログラミング言語の一つ。perl, ruby の仲間。
sympy: Mathematica,Maximaのような、数式処理(記号計算)をするためのpythonのライブラリ。
pythontex: texのコードのなかに、pythonのコードを書き実行させるもの。texliveの中に入っています。

これらを使って、
例えば、「因数分解」「整式の計算」の問題式と答えを出力(dvi)するtexのコードを書いてみます。
\pyc{} は python の command。
sympy.latex() は sympyの数式を、texの数式コードに変換する関数。
+ は文字列を連結する演算子。
print() は文字列を出力する関数。ここでは、texコードを出力。
他は、なんとなく分かると思います。

\pyc{expr=-12-8*x+15*x**2}
因数分解:~\pyc{print('$'+sympy.latex(expr)+'='+sympy.latex(sympy.factor(expr))+'$')}\\

%dvi出力--> 因数分解: 15x2-8x-12=(3x+2)(5x-6)


\pyc{expr=2*x+3*x}
整式の計算:~\pyc{print('$'+sympy.latex(expr)+'='+sympy.latex(sympy.simplify(expr))+'$')}

%dvi出力--> 整式の計算: 5x=5x 


できるのですが、後半は
2x+3x=5x とならずに 5x=5x となってしまいます。

つまり、sympyは式を評価し、アルファベット順・降べきの順に整理してしまうのです。
これでは、問題が作れない!

解決方法は?

もし、関心と時間のある方がおられましたら、
私(pythonfan)のブログ3本(添付ファイルあり)

「フォーラム内のブログ」 と、 http://pythonfan.tou3.com (忍者ブログ)にあります。
同じものです。

pythontexで高校数学の教材を作る --少し長い
pythontex+latex2sympyで高校数学の教材を作る
pythontex+latex2sympyで高校数学の教材を作る(その2)

を読んでいただき、ご意見(勘違いしていること、改善点、等々)を頂けたら有難いのですが、...。


pythonfan


pythonfan への返信

Re: pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- 前田 一貴 の投稿
興味はあったのですが,遅くなってしまって申し訳ありません.

http://docs.sympy.org/latest/modules/core.html

を見ながら試してみましたが,以下でどうでしょうか?

expr = sympy.sympify('2*x+3*x', evaluate=False)
print('$'+sympy.latex(expr)+'='+sympy.latex(sympy.simplify(expr))+'$')
前田 一貴 への返信

Re: pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- 前田 一貴 の投稿
質問かと勘違いしたのですが,ブログにある程度答えは書かれているのですね.
ベキや文字の順がソートされるのも問題だとすると,どうすればよいのでしょうね.
evaluate=False でもソートはされてしまうようです.
前田 一貴 への返信

Re: pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- pythonfan の投稿
sympy はアルファベット順・降べきの順に整理してしまうのです。


import sympy

sympy.var("a:z")

expr = sympy.sympify('2*x+3*x', evaluate=False)
print('$'+sympy.latex(expr)+'='+sympy.latex(sympy.simplify(expr))+'$')
#--> $2 x + 3 x=5 x$ 〇

expr = sympy.sympify('x**2+2*x -8', evaluate=False)
print('$'+sympy.latex(expr)+'='+sympy.latex(sympy.factor(expr))+'$')
#--> $x^{2} + 2 x - 8=\left(x - 2\right) \left(x + 4\right)$ 〇

expr = sympy.sympify('x**3+y**3+z**3 -3*x*y*z', evaluate=False)
print('$'+sympy.latex(expr)+'='+sympy.latex(sympy.factor(expr))+'$')
#--> $x^{3} - 3 x y z + y^{3} + z^{3}=\left(x + y + z\right) \left(x^{2} - x y - x z + y^{2} - y z + z^{2}\right)$ △
#--> $x^{3} + y^{3} + z^{3}- 3 x y z =\left(x + y + z\right) \left(x^{2} - x y - x z + y^{2} - y z + z^{2}\right)$ の形にしたい!

仕方がないので、latex2sympy を使うことになってしまったのです。
もっと簡単な方法があればよいのですが。

pythonfan への返信

Re: pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- 前田 一貴 の投稿
Sympy の入力の構文解析は parse_expr() という関数が行っているようです.
http://docs.sympy.org/latest/modules/parsing.html

この解説を読むと,evaluate=False の場合には順序もソートされないということがわかります.

>>> sympy.sympify('x+1').args
(1, x)
>>> sympy.sympify('1+x').args
(1, x)
>>> sympy.sympify('x+1', evaluate=False).args
(x, 1)
>>> sympy.sympify('1+x', evaluate=False).args
(1, x)

なお,Sympy の内部データ構造についての解説は以下です.
http://docs.sympy.org/latest/tutorial/manipulation.html

さて,これにも関わらず sympy.latex() で出力するとソートされてしまうのは,
sympy.latex() に渡してから実際の出力の間のどこかの段階でソート処理がかかっているためであると考えられます.
そこで,出力処理が書かれている LaTeXPrinter のコードを読んでみます.
http://docs.sympy.org/dev/_modules/sympy/printing/latex.html#LatexPrinter

Add オブジェクトの出力処理は _print_Add() に書かれているようです.
これを読むと,self.order が 'none' だったら args をリスト化してそのまま処理し,
そうでない場合は _as_ordered_terms() という関数を通すことがわかります.
名前からして,おそらく _as_ordered_terms() がソートをする関数なので,
order = 'none' を指定しておけばよいのだろう,ということになります.

>>> expr = sympy.sympify('x**3+y**3+z**3 -3*x*y*z', evaluate=False)
>>> print('$'+sympy.latex(expr, order='none')+'='+sympy.latex(sympy.factor(expr))+'$')
$x^{3} + y^{3} + z^{3} - 3 x y z=\left(x + y + z\right) \left(x^{2} - x y - x z + y^{2} - y z + z^{2}\right)$

なお,デフォルトでソートしないようにするには,

>>> sympy.init_printing(order='none')

とします.

>>> print('$'+sympy.latex(expr)+'='+sympy.latex(sympy.factor(expr))+'$')
$x^{3} + y^{3} + z^{3} - 3 x y z=\left(x + y + z\right) \left(x^{2} + y^{2} + z^{2} - x y - x z - y z\right)$

今度は右辺の順序も変わりました.
結果は場合に応じて順序を指定したいこともあるでしょうが,'lex' などを指定するでしょうか.

>>> print('$'+sympy.latex(expr)+'='+sympy.latex(sympy.factor(expr), order='lex')+'$')
$x^{3} + y^{3} + z^{3} - 3 x y z=\left(x + y + z\right) \left(x^{2} - x y - x z + y^{2} - y z + z^{2}\right)$

'lex' と 'grlex' は動いたけど,'rlex' と 'glex' が動かないのはなぜだろう…….
前田 一貴 への返信

Re: pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- pythonfan の投稿
前田一貴様

pythonで確認しました。
その方法で変数の順序を制御するのですね。
私の全く知らないことを教えていただき、有難うございます。
今年の3月にpythonを使い始めたので、はっきり言って知らないことばかりです。

その他では、
()がなくなってしまう問題。
sin(x), log(x) のように()がついてしまう問題。replace,re等である程度は解決できるが、...
余白を入れて式を整えたい問題。

例えば、

from sympy import *
var('a:z')

expr = sympify('(2*x+3)+(4*x+5)', evaluate=False)
print('$'+latex(expr, order='none')+'='+latex(simplify(expr))+'$')
#-->$2 x + 3 + 4 x + 5=6 x + 8$
#-->$(2 x + 3) + (4 x + 5)=6 x + 8$の形にしたい

expr = sympify('Integral((x+1)*cos(x), x)', evaluate=False)
print('$'+latex(expr, order='none')+'='+latex(expr.doit())+'$')
#-->$\int \left(x + 1\right) \cos{\left (x \right )}\, dx=x \sin{\left (x \right )} + \sin{\left (x \right )} + \cos{\left (x \right )}$ (x+1)cos x の形にしたい

細かいところで、sympy.latex()は融通が利かないところがあると思うのですが、...。






pythonfan への返信

Re: pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- 前田 一貴 の投稿
sin の括弧とかは,fold_func_brackets=True で省けるようです.
http://docs.sympy.org/dev/modules/printing.html#sympy.printing.latex.latex

>>> expr = sympify('Integral((x+1)*cos(x), x)', evaluate=False)
>>> print('$'+latex(expr, order='none')+'='+latex(expr.doit())+'$')
$\int \left(x + 1\right) \cos{\left (x \right )}\, dx=x \sin{\left (x \right )} + \sin{\left (x \right )} + \cos{\left (x \right )}$
>>> print('$'+latex(expr, order='none', fold_func_brackets=True)+'='+latex(expr.doit(), fold_func_brackets=True)+'$')
$\int \left(x + 1\right) \cos {x}\, dx=x \sin {x} + \sin {x} + \cos {x}$

一方で,

> $(2 x + 3) + (4 x + 5)=6 x + 8$の形にしたい

これは難しいかもしれません.

>>> sympify('(2*x+3)+(4*x+5)', evaluate=False).args
(2*x, 3, 4*x, 5)

という感じで,通常だと sympify した瞬間に括弧の構造が消えてしまいます.
苦肉の策として,自分で木構造を作ることにします.

>>> expr = Add(Add(2*x+3), Add(4*x+5), evaluate=False)
>>> expr.args
(2*x + 3, 4*x + 5)

これを latex() で出力すると,

>>> print(latex(expr))
2 x + 3 + 4 x + 5

とやはり括弧が外されてしまいます.
Add オブジェクトの出力で括弧をつけるかどうかは _needs_add_brackets() という関数で
制御されていますので,これを上書きするぐらいしか思いつきません.

>>> from sympy.printing.latex import LatexPrinter
>>> def _needs_add_brackets(self, expr):
...     return expr.is_Relational or expr.is_Add
...
>>> LatexPrinter._needs_add_brackets = _needs_add_brackets
>>> print(latex(expr))
\left(2 x + 3\right) + \left(4 x + 5\right)

あまり良い方法ではなさそうに思います.
前田 一貴 への返信

Re: pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- 前田 一貴 の投稿
sympy.parsing.sympy_parser.EvaluateFalseTransformer を改造して括弧の構造を
保存できないかと思って少し試してみましたが,ast モジュールの使い方を
ちゃんと理解できていないため,思い通りの処理が書けませんでした.
今はこれ以上時間を割く余裕がないので,私の中では一旦保留としておきます.
前田 一貴 への返信

Re: pythontexに関心のある方がおられましたら、 pythonfanブログを読んでいただいて、ご意見をお願いします。(再)

- pythonfan の投稿
前田一貴様、私のブログを読んでいただいた皆様へ


ブログを読んでいただき、ありがとうございます。
ご指摘いただいた点を訂正し、更に1本書きました。
定数π、eの決め方と、数列の、添え字表現→関数表現 の変換、等々てす。
(久しぶりに正規表現を使ってしまいました)

ブログが2か所にあるのも煩雑なので、「TeXフォーラム」のブログは削除し、
忍者ブログhttp://pythonfan.tou3.comのみとします。