Merge branch 'rtoy-wrap-option-args'
[maxima.git] / doc / info / pytranslate.texi
blob7f1ccd8e42f35854f39f4c02bc2191c55d2747d7
1 @menu
2 * Introduction to pytranslate::
3 * Functions in pytranslate::
4 * Extending pytranslate::
5 @end menu
7 @node Introduction to pytranslate, Functions in pytranslate, , Package pytranslate
8 @section Introduction to pytranslate
10 @code{pytranslate} package provides Maxima to Python translation functionality. The package is experimental, and the specifications of the functions in this package might change. It was written as a Google Summer of Code project by Lakshya A Agrawal (Undergraduate Student, IIIT-Delhi) in 2019. A detailed project report is available as a @url{https://gist.github.com/LakshyAAAgrawal/33eee2d33c4788764087eef1fa67269e, GitHub Gist}.@*
12 The package needs to be loaded in a Maxima instance for use, by executing @code{load("pytranslate");}@*
13 The statements are converted to python3 syntax. The file pytranslate.py must be imported for all translations to run, as shown in example.
15 Example:
16 @c ===beg===
17 @c load ("pytranslate")$
18 @c /* Define an example function to calculate factorial */
19 @c pytranslate(my_factorial(x) := if (x = 1 or x = 0) then 1
20 @c                  else x * my_factorial(x - 1));
21 @c my_factorial(5);
22 @c ===end===
23 @example
24 (%i1) load ("pytranslate")$
25 @group
26 /* Define an example function to calculate factorial */
27 (%i2) pytranslate(my_factorial(x) := if (x = 1 or x = 0) then 1
28                   else x * my_factorial(x - 1));
29 (%o2) 
30 def my_factorial(x, v = v):
31     v = Stack(@{@}, v)
32     v.ins(@{"x" : x@})
33     return((1 if ((v["x"] == 1) or (v["x"] == 0)) \
34               else (v["x"] * my_factorial((v["x"] + (-1))))))
35 m["my_factorial"] = my_factorial
36 @end group
37 @group
38 (%i3) my_factorial(5);
39 (%o3)                          120
40 @end group
41 @group
42 >>> from pytranslate import *
43 >>> def my_factorial(x, v = v):
44 ...     v = Stack(@{@}, v)
45 ...     v.ins(@{"x" : x@})
46 ...     return((1 if ((v["x"] == 1) or (v["x"] == 0)) \
47 ...     else (v["x"] * my_factorial((v["x"] + (-1))))))
48 ... 
49 >>> my_factorial(5)
50 120
51 @end group
52 @end example
54 The Maxima to Python Translator works in two stages:@*
55 1. Conversion of the internal Maxima representation to a defined Intermediate Representation, henceforth referred as IR(mapping is present in @file{share/pytranslate/maxima-to-ir.html})@*
56 2. The conversion of IR to Python.
58 Supported Maxima forms:@*
59 1. @mref{Numbers}(including complex numbers)@*
60 2. @mref{Assignment operators}@*
61 3. @mref{Arithmetic operators}(+, -, *, ^, /, !)@*
62 4. @mref{Logical operators}(and, or, not)@*
63 5. @mref{Relational operators}(@code{>}, @code{<}, @code{>=}, @code{<=}, @code{!=}, @code{==})@*
64 6. @mref{Lists}@*
65 7. @mref{Arrays}@*
66 8. @mref{block}@*
67 9. @mref{Function} and function calls@*
68 10. @mref{if}-else converted to Python conditionals@*
69 11. @mref{for} loops@*
70 12. @mref{lambda} form
72 @subsection Tests for pytranslate
73 The tests for @code{pytranslate} are present at @file{share/pytranslate/rtest_pytranslate.mac} and can be run by executing @code{batch(rtest_pytranslate, test);}
75 @node Functions in pytranslate, Extending pytranslate, Introduction to pytranslate, Package pytranslate
76 @section Functions in pytranslate
78 @deffn {Function} pytranslate (@var{expr}, [print-ir])
79 Translates the expression @var{expr} to equivalent python3 statements. Output is printed in the stdout.
81 Example:
82 @c ===beg===
83 @c load ("pytranslate")$
84 @c pytranslate('(for i:8 step -1 unless i<3 do (print(i))));
85 @c ===end===
86 @example
87 (%i1) load ("pytranslate")$
88 @group
89 (%i2) pytranslate('(for i:8 step -1 unless i<3 do (print(i))));
90 (%o2) 
91 v["i"] = 8
92 while not((v["i"] < 3)):
93     m["print"](v["i"])
94     v["i"] = (v["i"] + -1)
95 del v["i"]
96 @end group
97 @end example
99 @var{expr} is evaluated, and the return value is used for translation. Hence, for statements like assignment, it might be useful to quote the statement:
100 @c ===beg===
101 @c load ("pytranslate")$
102 @c pytranslate(x:20);
103 @c pytranslate('(x:20));
104 @c ===end===
105 @example
106 (%i1) load ("pytranslate")$
107 @group
108 (%i2) pytranslate(x:20);
109 (%o2) 
111 @end group
112 @group
113 (%i3) pytranslate('(x:20));
114 (%o3) 
115 v["x"] = 20
116 @end group
117 @end example
119 Passing the optional parameter (@var{print-ir}) to @code{pytranslate} as t, will print the internal IR representation of @code{expr} and return the translated python3 code.
121 @c ===beg===
122 @c load("pytranslate");
123 @c pytranslate('(plot3d(lambda([x, y], x^2+y^(-1)), [x, 1, 10],
124 @c                    [y, 1, 10])), t);
125 @c ===end===
126 @example
127 @group
128 (%i1) load("pytranslate");
129 (%o1) pytranslate
130 @end group
131 @group
132 (%i2) pytranslate('(plot3d(lambda([x, y], x^2+y^(-1)), [x, 1, 10],
133                    [y, 1, 10])), t);
134 (body
135  (funcall (element-array "m" (string "plot3d"))
136           (lambda
137               ((symbol "x") (symbol "y")
138                (op-no-bracket
139                 =
140                 (symbol "v")
141                 (funcall (symbol "stack") (dictionary) (symbol "v"))))
142             (op +
143                 (funcall (element-array (symbol "m") (string "pow"))
144                          (symbol "x") (num 2 0))
145                 (funcall (element-array (symbol "m") (string "pow"))
146                          (symbol "y") (unary-op - (num 1 0)))))
147           (struct-list (string "x") (num 1 0) (num 10 0))
148           (struct-list (string "y") (num 1 0) (num 10 0))))
149 (%o2) 
150 m["plot3d"](lambda x, y, v = Stack(@{@}, v): (m["pow"](x, 2) + m["\
151 pow"](y, (-1))), ["x", 1, 10], ["y", 1, 10])
152 @end group
153 @end example
154 @end deffn
156 @deffn {Function} show_form (@var{expr})
157 Displays the internal maxima form of @code{expr}
158 @example
159 @group
160 (%i4) show_form(a^b);
161 ((mexpt) $a $b) 
162 (%o4) a^b
163 @end group
164 @end example
165 @end deffn
167 @node Extending pytranslate,  , Functions in pytranslate, Package pytranslate
168 @section Extending pytranslate
169 Working of pytranslate:
170 @itemize @bullet
171 @item
172 The entry point for pytranslate is the function @code{$pytranslate} defined in @file{share/pytranslate/pytranslate.lisp}.
173 @item
174 @code{$pytranslate} calls the function @code{maxima-to-ir} with the Maxima expression as an argument(henceforth referred as @code{expr}).
175 @item
176 @code{maxima-to-ir} determines if @code{expr} is atomic or non-atomic(lisp cons form). If atomic, @code{atom-to-ir} is called with @code{expr} which returns the IR for the atomic expression.@*
177 To define/modify translation for atomic expressions, make changes to the definition of @code{atom-to-ir} in accordance with the IR.
178 @item
179 If @code{expr} is non-atomic, the function @code{cons-to-ir} is called with @code{expr} as an argument.@*
180 @itemize
181 @item
182 @code{cons-to-ir} looks for @code{(caar expr)} which specifies the type of @code{expr}, in hash-table @var{*maxima-direct-ir-map*} and if the type is found, then appends the retrieved IR with the result of lisp call @code{(mapcar #'maxima-to-ir (cdr expr))}, which applies maxima-to-ir function to all the elements present in the list. Effectively, recursively generate IR for all the elements present in @code{expr} and append them to the IR map for the type.@*
183 Example:
184 @example
185 @group
186 (%i9) show_form(a+b);
187 ((MPLUS) $B $A)
188 @end group
189 @group
190 (%i10) pytranslate(a+b, t);
191 (body (op + (element-array (symbol "v") (string "b")) \
192 (element-array (symbol "v") (string "a"))))
193 (%o10) 
194 (v["b"] + v["a"])
195 @end group
196 @end example
197 Here, operator + with internal maxima representation, @code{(mplus)} is present in @var{*maxima-direct-ir-map*} and mapped to @code{(op +)} to which the result of generating IR for all other elements of the list (a b), i.e. @code{(ELEMENT-ARRAY (SYMBOL "v") (STRING "b")) (ELEMENT-ARRAY (SYMBOL "v") (STRING "a"))} is appended.@*
199 @item
200 If @code{(caar expr)} is not found in @var{*maxima-direct-ir-map*}, then @code{cons-to-ir} looks for the type in @var{*maxima-special-ir-map*} which returns the function to handle the translation of the type of @code{expr}. @code{cons-to-ir} then calls the returned function with argument @code{expr} as an argument.@*
201 Example:
202 @example
203 @group
204 (%i11) show_form(g(x) := x^2);
205 ((mdefine simp) (($g) $x) ((mexpt) $x 2))
206 @end group
207 @group
208 (%i12) pytranslate(g(x):=x^2, t);
209 (body
210  (body
211   (func-def (symbol "g")
212             ((symbol "x") (op-no-bracket = (symbol "v") (symbol "v")))
213             (body-indented
214                 (op-no-bracket = (symbol "v") (funcall (symbol "stack") \
215                 (dictionary) (symbol "v")))
216                 (obj-funcall (symbol "v") (symbol "ins") (dictionary \
217                 ((string "x") (symbol "x"))))
218                 (funcall (symbol "return")
219                     (funcall (element-array (symbol "f") (string "pow"))
220                          (element-array (symbol "v") (string "x"))
221                                   (num 2 0)))))
222   (op-no-bracket = (element-array (symbol "f") (string "g")) \
223   (symbol "g"))))  
224 (%o12) 
225 def g(x, v = v):
226     v = Stack(@{@}, v)
227     v.ins(@{"x" : x@})
228     return(f["pow"](v["x"], 2))
229 f["g"] = g
231 @end group
232 @end example
233 Here, @code{mdefine}, which is the type of @code{expr} is present in @var{*maxima-special-ir-map*} which returns @code{func-def-to-ir} as handler function, which is then called with @code{expr} to generate the IR.@*
234 To define/modify translation for a type, add an entry to @var{*maxima-direct-ir-map*} if only a part of the IR needs to be generated and the rest can be appended, otherwise, for complete handling of @code{expr}, add an entry to @var{*maxima-special-ir-map*} and define a function with the name defined in @var{*maxima-special-ir-map*} which returns the IR for the form. The function naming convention for ir generators is (type)-to-ir, where type is the @code{(caar expr)} for expression(@code{mdefine -> func-def-to-ir}). The function must return a valid IR for the specific type.
235 @end itemize
236 @item
237 After the generation of IR, the function @code{ir-to-python} is called with the generated @code{ir} as an argument, which performs the codegen in a recursive manner.
238 @itemize
239 @item
240 @code{ir-to-python} looks for lisp @code{(car ir)} in the hash-table @var{*ir-python-direct-templates*}, which maps IR type to function handlers and calls the function returned with @code{ir} as an argument.
241 @end itemize
242 @item
243 To extend the IR of pytranslate, define a function with the naming convention (type)-to-python and add the name to @var{*ir-python-direct-templates*}.
244 @end itemize