1314 lines
34 KiB
ReStructuredText
1314 lines
34 KiB
ReStructuredText
|
|
Symbolic Evaluation with SymPy
|
|
==============================
|
|
|
|
.. code:: ipython2
|
|
|
|
import sympy
|
|
|
|
from joy.joy import run
|
|
from joy.library import UnaryBuiltinWrapper
|
|
from joy.utils.pretty_print import TracePrinter
|
|
from joy.utils.stack import list_to_stack
|
|
|
|
from notebook_preamble import D, J, V, define
|
|
|
|
.. code:: ipython2
|
|
|
|
sympy.init_printing()
|
|
|
|
`SypPy Symbols <http://docs.sympy.org/latest/modules/core.html#module-sympy.core.symbol>`__
|
|
-------------------------------------------------------------------------------------------
|
|
|
|
The SymPy package provides a powerful and elegant
|
|
`"thunk" <https://en.wikipedia.org/wiki/Thunk>`__ object that can take
|
|
the place of a numeric value in calculations and "record" the operations
|
|
performed on it.
|
|
|
|
We can create some of these objects and put them on the Joy stack:
|
|
|
|
.. code:: ipython2
|
|
|
|
stack = list_to_stack(sympy.symbols('c a b'))
|
|
|
|
If we evaluate the ``quadratic`` program
|
|
|
|
::
|
|
|
|
over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
|
|
|
|
The `SypPy
|
|
Symbols <http://docs.sympy.org/latest/modules/core.html#module-sympy.core.symbol>`__
|
|
will become the symbolic expression of the math operations.
|
|
Unfortunately, the library ``sqrt`` function doesn't work with the SymPy
|
|
objects:
|
|
|
|
.. code:: ipython2
|
|
|
|
viewer = TracePrinter()
|
|
try:
|
|
run('over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2', stack, D, viewer.viewer)
|
|
except Exception, e:
|
|
print e
|
|
viewer.print_()
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
can't convert expression to float
|
|
b a c . over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
|
|
b a c a . [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
|
|
b a c a [[[neg] dupdip sqr 4] dipd * * - sqrt pm] . dip 2 * [/] cons app2
|
|
b a c . [[neg] dupdip sqr 4] dipd * * - sqrt pm a 2 * [/] cons app2
|
|
b a c [[neg] dupdip sqr 4] . dipd * * - sqrt pm a 2 * [/] cons app2
|
|
b . [neg] dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
|
|
b [neg] . dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
|
|
b . neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
|
|
-b . b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
|
|
-b b . sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
|
|
-b b . dup mul 4 a c * * - sqrt pm a 2 * [/] cons app2
|
|
-b b b . mul 4 a c * * - sqrt pm a 2 * [/] cons app2
|
|
-b b**2 . 4 a c * * - sqrt pm a 2 * [/] cons app2
|
|
-b b**2 4 . a c * * - sqrt pm a 2 * [/] cons app2
|
|
-b b**2 4 a . c * * - sqrt pm a 2 * [/] cons app2
|
|
-b b**2 4 a c . * * - sqrt pm a 2 * [/] cons app2
|
|
-b b**2 4 a*c . * - sqrt pm a 2 * [/] cons app2
|
|
-b b**2 4*a*c . - sqrt pm a 2 * [/] cons app2
|
|
-b -4*a*c + b**2 . sqrt pm a 2 * [/] cons app2
|
|
|
|
|
|
We can pick out that first symbolic expression obect from the Joy stack:
|
|
|
|
.. code:: ipython2
|
|
|
|
S, E = viewer.history[-1]
|
|
|
|
.. code:: ipython2
|
|
|
|
q = S[0]
|
|
q
|
|
|
|
|
|
|
|
|
|
.. math::
|
|
|
|
- 4 a c + b^{2}
|
|
|
|
|
|
|
|
The Python ``math.sqrt()`` function causes the "can't convert expression
|
|
to float" exception but ``sympy.sqrt()`` does not:
|
|
|
|
.. code:: ipython2
|
|
|
|
sympy.sqrt(q)
|
|
|
|
|
|
|
|
|
|
.. math::
|
|
|
|
\sqrt{- 4 a c + b^{2}}
|
|
|
|
|
|
|
|
Use ``sympy.sqrt``
|
|
------------------
|
|
|
|
This is easy to fix.
|
|
|
|
.. code:: ipython2
|
|
|
|
D['sqrt'] = UnaryBuiltinWrapper(sympy.sqrt)
|
|
|
|
Now it works just fine.
|
|
|
|
.. code:: ipython2
|
|
|
|
(root1, (root2, _)) = run('over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2', stack, D)[0]
|
|
|
|
.. code:: ipython2
|
|
|
|
root1
|
|
|
|
|
|
|
|
|
|
.. math::
|
|
|
|
\frac{1}{2 a} \left(- b - \sqrt{- 4 a c + b^{2}}\right)
|
|
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
root2
|
|
|
|
|
|
|
|
|
|
.. math::
|
|
|
|
\frac{1}{2 a} \left(- b + \sqrt{- 4 a c + b^{2}}\right)
|
|
|
|
|
|
|
|
At some point I will probably make an optional library of Joy wrappers
|
|
for SymPy functions, and either load it automatically if SymPy
|
|
installation is available or have a CLI switch or something. There's a
|
|
huge amount of incredibly useful stuff and I don't see why Joy shouldn't
|
|
expose another interface for using it. (As an example, the symbolic
|
|
expressions can be "lambdafied" into very fast versions, i.e. a function
|
|
that takes ``a``, ``b``, and ``c`` and computes the value of the root
|
|
using just low-level fast code, bypassing Joy and Python. Also, Numpy,
|
|
&c.)
|
|
|
|
Partial Evaluation
|
|
------------------
|
|
|
|
`Futamura
|
|
projections <https://en.wikipedia.org/wiki/Futamura_projection>`__
|
|
|
|
Starting with the example from `Partial Computation of
|
|
Programs <http://hdl.handle.net/2433/103401>`__ by `Yoshihiko
|
|
Futamura <http://fi.ftmr.info/>`__ of a function to compute ``u`` to the
|
|
``k``\ th power:
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(u, k):
|
|
z = 1
|
|
while k != 0:
|
|
if odd(k):
|
|
z = z * u
|
|
k = k / 2
|
|
u = u * u
|
|
return z
|
|
|
|
Partial evaluation with ``k = 5``:
|
|
|
|
.. code:: ipython2
|
|
|
|
def F5(u):
|
|
z = 1 * u
|
|
u = u * u
|
|
u = u * u
|
|
z = z * u
|
|
return z
|
|
|
|
Translate ``F(u, k)`` to Joy
|
|
|
|
::
|
|
|
|
u k 1 # z = 1
|
|
[pop] [Fw] while # the while statement
|
|
popopd # discard u k, "return" z
|
|
|
|
What's Fw?
|
|
|
|
::
|
|
|
|
u k z [pop odd] [Ft] [] ifte # the if statement
|
|
[2 //] dip # k = k / 2 floordiv
|
|
[sqr] dipd # u = u * u
|
|
|
|
[[sqr] dip 2 //] dip # We can merge last two lines.
|
|
|
|
Helper function Ft (to compute z = z \* u).
|
|
|
|
::
|
|
|
|
u k z Ft
|
|
---------------
|
|
u k u*z
|
|
|
|
|
|
Ft == [over] dip *
|
|
|
|
Putting it together:
|
|
|
|
::
|
|
|
|
Ft == [over] dip *
|
|
Fb == [[sqr] dip 2 //] dip
|
|
Fw == [pop odd] [Ft] [] ifte Fb
|
|
F == 1 [pop] [Fw] while popopd
|
|
|
|
.. code:: ipython2
|
|
|
|
define('odd == 2 %')
|
|
|
|
.. code:: ipython2
|
|
|
|
define('Ft == [over] dip *')
|
|
|
|
.. code:: ipython2
|
|
|
|
define('Fb == [[sqr] dip 2 //] dip')
|
|
|
|
.. code:: ipython2
|
|
|
|
define('Fw == [pop odd] [Ft] [] ifte Fb')
|
|
|
|
.. code:: ipython2
|
|
|
|
define('F == 1 [pop] [Fw] while popopd')
|
|
|
|
Try it out:
|
|
|
|
.. code:: ipython2
|
|
|
|
J('2 5 F')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
32
|
|
|
|
|
|
In order to elide the tests let's define special versions of ``while``
|
|
and ``ifte``:
|
|
|
|
.. code:: ipython2
|
|
|
|
from joy.joy import joy
|
|
from joy.library import FunctionWrapper
|
|
from joy.parser import Symbol
|
|
from joy.utils.stack import concat
|
|
|
|
|
|
S_while = Symbol('while')
|
|
|
|
|
|
@FunctionWrapper
|
|
def while_(S, expression, dictionary):
|
|
'''[if] [body] while'''
|
|
(body, (if_, stack)) = S
|
|
if joy(stack, if_, dictionary)[0][0]:
|
|
expression = concat(body, (if_, (body, (S_while, expression))))
|
|
return stack, expression, dictionary
|
|
|
|
|
|
@FunctionWrapper
|
|
def ifte(stack, expression, dictionary):
|
|
'''[if] [then] [else] ifte'''
|
|
(else_, (then, (if_, stack))) = stack
|
|
if_res = joy(stack, if_, dictionary)[0][0]
|
|
quote = then if if_res else else_
|
|
expression = concat(quote, expression)
|
|
return (stack, expression, dictionary)
|
|
|
|
|
|
D['ifte'] = ifte
|
|
D['while'] = while_
|
|
|
|
And with a SymPy symbol for the ``u`` argument:
|
|
|
|
.. code:: ipython2
|
|
|
|
stack = list_to_stack([5, sympy.symbols('u')])
|
|
viewer = TracePrinter()
|
|
try:
|
|
(result, _) = run('F', stack, D, viewer.viewer)[0]
|
|
except Exception, e:
|
|
print e
|
|
viewer.print_()
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
u 5 . F
|
|
u 5 . 1 [pop] [Fw] while popopd
|
|
u 5 1 . [pop] [Fw] while popopd
|
|
u 5 1 [pop] . [Fw] while popopd
|
|
u 5 1 [pop] [Fw] . while popopd
|
|
u 5 1 . Fw [pop] [Fw] while popopd
|
|
u 5 1 . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u 5 1 [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u 5 1 [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
|
|
u 5 1 [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
|
|
u 5 1 . Ft Fb [pop] [Fw] while popopd
|
|
u 5 1 . [over] dip * Fb [pop] [Fw] while popopd
|
|
u 5 1 [over] . dip * Fb [pop] [Fw] while popopd
|
|
u 5 . over 1 * Fb [pop] [Fw] while popopd
|
|
u 5 u . 1 * Fb [pop] [Fw] while popopd
|
|
u 5 u 1 . * Fb [pop] [Fw] while popopd
|
|
u 5 u . Fb [pop] [Fw] while popopd
|
|
u 5 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
|
|
u 5 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
|
|
u 5 . [sqr] dip 2 // u [pop] [Fw] while popopd
|
|
u 5 [sqr] . dip 2 // u [pop] [Fw] while popopd
|
|
u . sqr 5 2 // u [pop] [Fw] while popopd
|
|
u . dup mul 5 2 // u [pop] [Fw] while popopd
|
|
u u . mul 5 2 // u [pop] [Fw] while popopd
|
|
u**2 . 5 2 // u [pop] [Fw] while popopd
|
|
u**2 5 . 2 // u [pop] [Fw] while popopd
|
|
u**2 5 2 . // u [pop] [Fw] while popopd
|
|
u**2 2 . u [pop] [Fw] while popopd
|
|
u**2 2 u . [pop] [Fw] while popopd
|
|
u**2 2 u [pop] . [Fw] while popopd
|
|
u**2 2 u [pop] [Fw] . while popopd
|
|
u**2 2 u . Fw [pop] [Fw] while popopd
|
|
u**2 2 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u**2 2 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u**2 2 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
|
|
u**2 2 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
|
|
u**2 2 u . Fb [pop] [Fw] while popopd
|
|
u**2 2 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
|
|
u**2 2 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
|
|
u**2 2 . [sqr] dip 2 // u [pop] [Fw] while popopd
|
|
u**2 2 [sqr] . dip 2 // u [pop] [Fw] while popopd
|
|
u**2 . sqr 2 2 // u [pop] [Fw] while popopd
|
|
u**2 . dup mul 2 2 // u [pop] [Fw] while popopd
|
|
u**2 u**2 . mul 2 2 // u [pop] [Fw] while popopd
|
|
u**4 . 2 2 // u [pop] [Fw] while popopd
|
|
u**4 2 . 2 // u [pop] [Fw] while popopd
|
|
u**4 2 2 . // u [pop] [Fw] while popopd
|
|
u**4 1 . u [pop] [Fw] while popopd
|
|
u**4 1 u . [pop] [Fw] while popopd
|
|
u**4 1 u [pop] . [Fw] while popopd
|
|
u**4 1 u [pop] [Fw] . while popopd
|
|
u**4 1 u . Fw [pop] [Fw] while popopd
|
|
u**4 1 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u**4 1 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u**4 1 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
|
|
u**4 1 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
|
|
u**4 1 u . Ft Fb [pop] [Fw] while popopd
|
|
u**4 1 u . [over] dip * Fb [pop] [Fw] while popopd
|
|
u**4 1 u [over] . dip * Fb [pop] [Fw] while popopd
|
|
u**4 1 . over u * Fb [pop] [Fw] while popopd
|
|
u**4 1 u**4 . u * Fb [pop] [Fw] while popopd
|
|
u**4 1 u**4 u . * Fb [pop] [Fw] while popopd
|
|
u**4 1 u**5 . Fb [pop] [Fw] while popopd
|
|
u**4 1 u**5 . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
|
|
u**4 1 u**5 [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
|
|
u**4 1 . [sqr] dip 2 // u**5 [pop] [Fw] while popopd
|
|
u**4 1 [sqr] . dip 2 // u**5 [pop] [Fw] while popopd
|
|
u**4 . sqr 1 2 // u**5 [pop] [Fw] while popopd
|
|
u**4 . dup mul 1 2 // u**5 [pop] [Fw] while popopd
|
|
u**4 u**4 . mul 1 2 // u**5 [pop] [Fw] while popopd
|
|
u**8 . 1 2 // u**5 [pop] [Fw] while popopd
|
|
u**8 1 . 2 // u**5 [pop] [Fw] while popopd
|
|
u**8 1 2 . // u**5 [pop] [Fw] while popopd
|
|
u**8 0 . u**5 [pop] [Fw] while popopd
|
|
u**8 0 u**5 . [pop] [Fw] while popopd
|
|
u**8 0 u**5 [pop] . [Fw] while popopd
|
|
u**8 0 u**5 [pop] [Fw] . while popopd
|
|
u**8 0 u**5 . popopd
|
|
u**5 .
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
result
|
|
|
|
|
|
|
|
|
|
.. math::
|
|
|
|
u^{5}
|
|
|
|
|
|
|
|
Let's try partial evaluation by hand and use a "stronger" thunk.
|
|
|
|
Caret underscoring indicates terms that form thunks. When an arg is
|
|
unavailable for a computation we just postpone it until the arg becomes
|
|
available and in the meantime treat the pending computation as one unit.
|
|
|
|
::
|
|
|
|
u 5 . F
|
|
u 5 . 1 [pop] [Fw] while popopd
|
|
u 5 1 . [pop] [Fw] while popopd
|
|
u 5 1 [pop] . [Fw] while popopd
|
|
u 5 1 [pop] [Fw] . while popopd
|
|
u 5 1 . Fw [pop] [Fw] while popopd
|
|
u 5 1 . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u 5 1 [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u 5 1 [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
|
|
u 5 1 [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
|
|
u 5 1 . Ft Fb [pop] [Fw] while popopd
|
|
u 5 1 . [over] dip * Fb [pop] [Fw] while popopd
|
|
u 5 1 [over] . dip * Fb [pop] [Fw] while popopd
|
|
u 5 . over 1 * Fb [pop] [Fw] while popopd
|
|
u 5 u . 1 * Fb [pop] [Fw] while popopd
|
|
u 5 u 1 . * Fb [pop] [Fw] while popopd
|
|
u 5 u . Fb [pop] [Fw] while popopd
|
|
u 5 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
|
|
u 5 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
|
|
u 5 . [sqr] dip 2 // u [pop] [Fw] while popopd
|
|
u 5 [sqr] . dip 2 // u [pop] [Fw] while popopd
|
|
u . sqr 5 2 // u [pop] [Fw] while popopd
|
|
u . dup mul 5 2 // u [pop] [Fw] while popopd
|
|
u dup * . 5 2 // u [pop] [Fw] while popopd
|
|
^^^^^^^
|
|
|
|
::
|
|
|
|
u dup * 2 u [pop] [Fw] . while popopd
|
|
u dup * 2 u . Fw [pop] [Fw] while popopd
|
|
u dup * 2 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u dup * 2 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
u dup * 2 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
|
|
u dup * 2 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
|
|
u dup * 2 u . Fb [pop] [Fw] while popopd
|
|
u dup * 2 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
|
|
u dup * 2 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
|
|
u dup * 2 . [sqr] dip 2 // u [pop] [Fw] while popopd
|
|
u dup * 2 [sqr] . dip 2 // u [pop] [Fw] while popopd
|
|
u dup * . sqr 2 2 // u [pop] [Fw] while popopd
|
|
u dup * . dup mul 2 2 // u [pop] [Fw] while popopd
|
|
u dup * dup * . 2 2 // u [pop] [Fw] while popopd
|
|
^^^^^^^^^^^^^
|
|
|
|
w/ ``K == u dup * dup *``
|
|
|
|
::
|
|
|
|
K 1 u [pop] [Fw] . while popopd
|
|
K 1 u . Fw [pop] [Fw] while popopd
|
|
K 1 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
K 1 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
|
|
K 1 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
|
|
K 1 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
|
|
K 1 u . Ft Fb [pop] [Fw] while popopd
|
|
K 1 u . [over] dip * Fb [pop] [Fw] while popopd
|
|
K 1 u [over] . dip * Fb [pop] [Fw] while popopd
|
|
K 1 . over u * Fb [pop] [Fw] while popopd
|
|
K 1 K . u * Fb [pop] [Fw] while popopd
|
|
K 1 K u . * Fb [pop] [Fw] while popopd
|
|
K 1 K u * . Fb [pop] [Fw] while popopd
|
|
^^^^^
|
|
|
|
w/ ``L == K u *``
|
|
|
|
::
|
|
|
|
K 1 L . Fb [pop] [Fw] while popopd
|
|
K 1 L . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
|
|
K 1 L [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
|
|
K 1 . [sqr] dip 2 // L [pop] [Fw] while popopd
|
|
K 1 [sqr] . dip 2 // L [pop] [Fw] while popopd
|
|
K . sqr 1 2 // L [pop] [Fw] while popopd
|
|
K . dup mul 1 2 // L [pop] [Fw] while popopd
|
|
K K . mul 1 2 // L [pop] [Fw] while popopd
|
|
K K * . 1 2 // L [pop] [Fw] while popopd
|
|
^^^^^
|
|
K K * . 1 2 // L [pop] [Fw] while popopd
|
|
K K * 1 . 2 // L [pop] [Fw] while popopd
|
|
K K * 1 2 . // L [pop] [Fw] while popopd
|
|
K K * 0 . L [pop] [Fw] while popopd
|
|
K K * 0 L . [pop] [Fw] while popopd
|
|
K K * 0 L [pop] . [Fw] while popopd
|
|
K K * 0 L [pop] [Fw] . while popopd
|
|
^^^^^
|
|
K K * 0 L . popopd
|
|
L .
|
|
|
|
So:
|
|
|
|
::
|
|
|
|
K == u dup * dup *
|
|
L == K u *
|
|
|
|
Our result "thunk" would be:
|
|
|
|
::
|
|
|
|
u dup * dup * u *
|
|
|
|
Mechanically, you could do:
|
|
|
|
::
|
|
|
|
u dup * dup * u *
|
|
u u [dup * dup *] dip *
|
|
u dup [dup * dup *] dip *
|
|
|
|
|
|
F5 == dup [dup * dup *] dip *
|
|
|
|
But we can swap the two arguments to the final ``*`` to get all mentions
|
|
of ``u`` to the left:
|
|
|
|
::
|
|
|
|
u u dup * dup * *
|
|
|
|
Then de-duplicate "u":
|
|
|
|
::
|
|
|
|
u dup dup * dup * *
|
|
|
|
To arrive at a startlingly elegant form for F5:
|
|
|
|
::
|
|
|
|
F5 == dup dup * dup * *
|
|
|
|
.. code:: ipython2
|
|
|
|
stack = list_to_stack([sympy.symbols('u')])
|
|
viewer = TracePrinter()
|
|
try:
|
|
(result, _) = run('dup dup * dup * *', stack, D, viewer.viewer)[0]
|
|
except Exception, e:
|
|
print e
|
|
viewer.print_()
|
|
result
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
u . dup dup * dup * *
|
|
u u . dup * dup * *
|
|
u u u . * dup * *
|
|
u u**2 . dup * *
|
|
u u**2 u**2 . * *
|
|
u u**4 . *
|
|
u**5 .
|
|
|
|
|
|
|
|
|
|
.. math::
|
|
|
|
u^{5}
|
|
|
|
|
|
|
|
I'm not sure how to implement these kinds of thunks. I think you have to
|
|
have support in the interpreter, or you have to modify all of the
|
|
functions like ``dup`` to check for thunks in their inputs.
|
|
|
|
Working on the compiler, from this:
|
|
|
|
::
|
|
|
|
dup dup * dup * *
|
|
|
|
We can already generate:
|
|
|
|
::
|
|
|
|
---------------------------------
|
|
(a0, stack) = stack
|
|
a1 = mul(a0, a0)
|
|
a2 = mul(a1, a1)
|
|
a3 = mul(a2, a0)
|
|
stack = (a3, stack)
|
|
---------------------------------
|
|
|
|
This is pretty old stuff... (E.g. from 1999, M. Anton Ertl `Compilation
|
|
of Stack-Based
|
|
Languages <http://www.complang.tuwien.ac.at/projects/rafts.html>`__ he
|
|
goes a lot further for Forth.)
|
|
|
|
"A Transformation Based Approach to Semantics-Directed Code Generation"
|
|
-----------------------------------------------------------------------
|
|
|
|
by Arthur Nunes-Harwitt
|
|
|
|
https://dl.acm.org/citation.cfm?doid=2635648.2635652
|
|
|
|
.. code:: ipython2
|
|
|
|
def m(x, y): return x * y
|
|
|
|
print m(2, 3)
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
6
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
def m(x): return lambda y: x * y
|
|
|
|
print m(2)(3)
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
6
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
def m(x): return "lambda y: %(x)s * y" % locals()
|
|
|
|
print m(2)
|
|
print eval(m(2))(3)
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
lambda y: 2 * y
|
|
6
|
|
|
|
|
|
In Joy:
|
|
|
|
::
|
|
|
|
m == [*] cons
|
|
|
|
3 2 m i
|
|
3 2 [*] cons i
|
|
3 [2 *] i
|
|
3 2 *
|
|
6
|
|
|
|
.. code:: ipython2
|
|
|
|
def p(n, b): # original
|
|
return 1 if n == 0 else b * p(n - 1, b)
|
|
|
|
|
|
def p(n): # curried
|
|
return lambda b: 1 if n == 0 else b * p(n - 1, b)
|
|
|
|
|
|
def p(n): # quoted
|
|
return "lambda b: 1 if %(n)s == 0 else b * p(%(n)s - 1, b)" % locals()
|
|
|
|
|
|
print p(3)
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
lambda b: 1 if 3 == 0 else b * p(3 - 1, b)
|
|
|
|
|
|
Original
|
|
|
|
::
|
|
|
|
p == [0 =] [popop 1] [-- over] [dip *] genrec
|
|
|
|
b n p
|
|
b n [0 =] [popop 1] [-- over [p] dip *]
|
|
|
|
b n -- over [p] dip *
|
|
b n-1 over [p] dip *
|
|
b n-1 b [p] dip *
|
|
b n-1 p b *
|
|
|
|
curried, quoted
|
|
|
|
::
|
|
|
|
n p
|
|
---------------------------------------------
|
|
[[n 0 =] [pop 1] [dup n --] [*] genrec]
|
|
|
|
.. code:: ipython2
|
|
|
|
def p(n): # lambda lowered
|
|
return (
|
|
lambda b: 1
|
|
if n == 0 else
|
|
lambda b: b * p(n - 1, b)
|
|
)
|
|
|
|
|
|
def p(n): # lambda lowered quoted
|
|
return (
|
|
"lambda b: 1"
|
|
if n == 0 else
|
|
"lambda b: b * p(%(n)s - 1, b)" % locals()
|
|
)
|
|
|
|
print p(3)
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
lambda b: b * p(3 - 1, b)
|
|
|
|
|
|
::
|
|
|
|
p == [0 =] [[pop 1]] [ [-- [dup] dip p *] cons ]ifte
|
|
|
|
|
|
3 p
|
|
3 [-- [dup] dip p *] cons
|
|
[3 -- [dup] dip p *]
|
|
|
|
.. code:: ipython2
|
|
|
|
def p(n): # expression lifted
|
|
if n == 0:
|
|
return lambda b: 1
|
|
f = p(n - 1)
|
|
return lambda b: b * f(b)
|
|
|
|
|
|
print p(3)(2)
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
8
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
def p(n): # quoted
|
|
if n == 0:
|
|
return "lambda b: 1"
|
|
f = p(n - 1)
|
|
return "lambda b: b * (%(f)s)(b)" % locals()
|
|
|
|
print p(3)
|
|
print eval(p(3))(2)
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
lambda b: b * (lambda b: b * (lambda b: b * (lambda b: 1)(b))(b))(b)
|
|
8
|
|
|
|
|
|
::
|
|
|
|
p == [0 =] [pop [pop 1]] [-- p [dupdip *] cons] ifte
|
|
|
|
|
|
3 p
|
|
3 -- p [dupdip *] cons
|
|
2 p [dupdip *] cons
|
|
2 -- p [dupdip *] cons [dupdip *] cons
|
|
1 p [dupdip *] cons [dupdip *] cons
|
|
1 -- p [dupdip *] cons [dupdip *] cons [dupdip *] cons
|
|
0 p [dupdip *] cons [dupdip *] cons [dupdip *] cons
|
|
0 pop [pop 1] [dupdip *] cons [dupdip *] cons [dupdip *] cons
|
|
[pop 1] [dupdip *] cons [dupdip *] cons [dupdip *] cons
|
|
...
|
|
[[[[pop 1] dupdip *] dupdip *] dupdip *]
|
|
|
|
|
|
2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i
|
|
2 [[[pop 1] dupdip *] dupdip *] dupdip *
|
|
2 [[pop 1] dupdip *] dupdip * 2 *
|
|
2 [pop 1] dupdip * 2 * 2 *
|
|
2 pop 1 2 * 2 * 2 *
|
|
1 2 * 2 * 2 *
|
|
|
|
|
|
|
|
p == [0 =] [pop [pop 1]] [-- p [dupdip *] cons] ifte
|
|
p == [0 =] [pop [pop 1]] [-- [p] i [dupdip *] cons] ifte
|
|
p == [0 =] [pop [pop 1]] [--] [i [dupdip *] cons] genrec
|
|
|
|
.. code:: ipython2
|
|
|
|
define('p == [0 =] [pop [pop 1]] [--] [i [dupdip *] cons] genrec')
|
|
|
|
.. code:: ipython2
|
|
|
|
J('3 p')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
[[[[pop 1] dupdip *] dupdip *] dupdip *]
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
V('2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
. 2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i
|
|
2 . [[[[pop 1] dupdip *] dupdip *] dupdip *] i
|
|
2 [[[[pop 1] dupdip *] dupdip *] dupdip *] . i
|
|
2 . [[[pop 1] dupdip *] dupdip *] dupdip *
|
|
2 [[[pop 1] dupdip *] dupdip *] . dupdip *
|
|
2 . [[pop 1] dupdip *] dupdip * 2 *
|
|
2 [[pop 1] dupdip *] . dupdip * 2 *
|
|
2 . [pop 1] dupdip * 2 * 2 *
|
|
2 [pop 1] . dupdip * 2 * 2 *
|
|
2 . pop 1 2 * 2 * 2 *
|
|
. 1 2 * 2 * 2 *
|
|
1 . 2 * 2 * 2 *
|
|
1 2 . * 2 * 2 *
|
|
2 . 2 * 2 *
|
|
2 2 . * 2 *
|
|
4 . 2 *
|
|
4 2 . *
|
|
8 .
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
stack = list_to_stack([sympy.symbols('u')])
|
|
(result, s) = run('p i', stack, D)[0]
|
|
result
|
|
|
|
From this:
|
|
|
|
::
|
|
|
|
p == [0 =] [pop pop 1] [-- over] [dip *] genrec
|
|
|
|
To this:
|
|
|
|
::
|
|
|
|
p == [0 =] [pop [pop 1]] [--] [i [dupdip *] cons] genrec
|
|
|
|
Try it with ``F()``:
|
|
--------------------
|
|
|
|
.. code:: ipython2
|
|
|
|
def odd(n): return n % 2
|
|
|
|
|
|
def F(u, k):
|
|
z = 1
|
|
while k != 0:
|
|
if odd(k):
|
|
z = z * u
|
|
k = k / 2
|
|
u = u * u
|
|
return z
|
|
|
|
F(2, 5)
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(k):
|
|
def _F(u, k=k):
|
|
z = 1
|
|
while k != 0:
|
|
if odd(k):
|
|
z = z * u
|
|
k = k / 2
|
|
u = u * u
|
|
return z
|
|
return _F
|
|
|
|
F(5)(2)
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(k):
|
|
def _F(u, k=k):
|
|
if k == 0:
|
|
z = 1
|
|
else:
|
|
z = F(k / 2)(u)
|
|
z *= z
|
|
if odd(k):
|
|
z = z * u
|
|
return z
|
|
return _F
|
|
|
|
F(5)(2)
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(k):
|
|
if k == 0:
|
|
z = lambda u: 1
|
|
else:
|
|
f = F(k / 2)
|
|
def z(u):
|
|
uu = f(u)
|
|
uu *= uu
|
|
return uu * u if odd(k) else uu
|
|
return z
|
|
|
|
F(5)(2)
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(k):
|
|
if k == 0:
|
|
z = lambda u: 1
|
|
else:
|
|
f = F(k / 2)
|
|
if odd(k):
|
|
z = lambda u: (lambda fu, u: fu * fu * u)(f(u), u)
|
|
else:
|
|
z = lambda u: (lambda fu, u: fu * fu)(f(u), u)
|
|
return z
|
|
|
|
F(5)(2)
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(k):
|
|
if k == 0:
|
|
z = "lambda u: 1"
|
|
else:
|
|
f = F(k / 2)
|
|
if odd(k):
|
|
z = "lambda u: (lambda fu, u: fu * fu * u)((%(f)s)(u), u)" % locals()
|
|
else:
|
|
z = "lambda u: (lambda fu, u: fu * fu)((%(f)s)(u), u)" % locals()
|
|
return z
|
|
|
|
source = F(5)
|
|
print source
|
|
eval(source)(2)
|
|
|
|
Hmm...
|
|
|
|
.. code:: ipython2
|
|
|
|
for n in range(4):
|
|
print F(n)
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(k):
|
|
if k == 0:
|
|
z = "lambda u: 1"
|
|
elif k == 1:
|
|
z = "lambda u: u"
|
|
else:
|
|
f = F(k / 2)
|
|
if odd(k):
|
|
z = "lambda u: (lambda fu, u: fu * fu * u)((%(f)s)(u), u)" % locals()
|
|
else:
|
|
z = "lambda u: (lambda fu, u: fu * fu)((%(f)s)(u), u)" % locals()
|
|
return z
|
|
|
|
source = F(5)
|
|
print source
|
|
eval(source)(2)
|
|
|
|
.. code:: ipython2
|
|
|
|
for n in range(4):
|
|
print F(n)
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(k):
|
|
if k == 0:
|
|
z = "lambda u: 1"
|
|
elif k == 1:
|
|
z = "lambda u: u"
|
|
else:
|
|
m = k / 2
|
|
if odd(k):
|
|
if m == 0:
|
|
z = "lambda u: 1"
|
|
elif m == 1:
|
|
z = "lambda u: u * u * u"
|
|
else:
|
|
z = "lambda u: (lambda fu, u: fu * fu * u)((%s)(u), u)" % F(m)
|
|
else:
|
|
if m == 0:
|
|
z = "lambda u: 1"
|
|
elif m == 1:
|
|
z = "lambda u: u * u"
|
|
else:
|
|
z = "lambda u: (lambda u: u * u)((%s)(u))" % F(m)
|
|
return z
|
|
|
|
source = F(5)
|
|
print source
|
|
eval(source)(2)
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(k):
|
|
if k == 0:
|
|
z = "lambda u: 1"
|
|
elif k == 1:
|
|
z = "lambda u: u"
|
|
else:
|
|
m = k / 2
|
|
if m == 0:
|
|
z = "lambda u: 1"
|
|
|
|
elif odd(k):
|
|
if m == 1:
|
|
z = "lambda u: u * u * u"
|
|
else:
|
|
z = "lambda u: (lambda fu, u: fu * fu * u)((%s)(u), u)" % F(m)
|
|
else:
|
|
if m == 1:
|
|
z = "lambda u: u * u"
|
|
else:
|
|
z = "lambda u: (lambda u: u * u)((%s)(u))" % F(m)
|
|
return z
|
|
|
|
source = F(5)
|
|
print source
|
|
eval(source)(2)
|
|
|
|
.. code:: ipython2
|
|
|
|
for n in range(7):
|
|
source = F(n)
|
|
print n, '%2i' % eval(source)(2), source
|
|
|
|
So that gets pretty good, eh?
|
|
|
|
But looking back at the definition in Joy, it doesn't seem easy to
|
|
directly apply this technique to Joy code:
|
|
|
|
::
|
|
|
|
Ft == [over] dip *
|
|
Fb == [[sqr] dip 2 //] dip
|
|
Fw == [pop odd] [Ft] [] ifte Fb
|
|
F == 1 [pop] [Fw] while popopd
|
|
|
|
But a direct translation of the Python code..?
|
|
|
|
::
|
|
|
|
F == [
|
|
[[0 =] [pop 1]]
|
|
[[1 =] []]
|
|
[_F.0]
|
|
] cond
|
|
|
|
_F.0 == dup 2 // [
|
|
[[0 =] [pop 1]]
|
|
[[pop odd] _F.1]
|
|
[_F.2]
|
|
] cond
|
|
|
|
_F.1 == [1 =] [pop [dup dup * *]] [popd F [dupdip over * *] cons] ifte
|
|
_F.2 == [1 =] [pop [dup *]] [popd F [i dup *] cons] ifte
|
|
|
|
Try it:
|
|
|
|
::
|
|
|
|
5 F
|
|
5 [ [[0 =] [pop 1]] [[1 =] []] [_F.0] ] cond
|
|
5 _F.0
|
|
5 dup 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
|
|
5 5 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
|
|
|
|
5 2 [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
|
|
5 2 _F.1
|
|
5 2 [1 =] [popop [dup dup * *]] [popd F [dupdip over * *] cons] ifte
|
|
5 2 popd F [dupdip over * *] cons
|
|
2 F [dupdip over * *] cons
|
|
|
|
2 F [dupdip over * *] cons
|
|
|
|
2 F
|
|
2 [ [[0 =] [pop 1]] [[1 =] []] [_F.0] ] cond
|
|
2 _F.0
|
|
2 dup 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
|
|
2 2 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
|
|
2 1 [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
|
|
2 1 _F.2
|
|
2 1 [1 =] [popop [dup *]] [popd F [i dup *] cons] ifte
|
|
2 1 popop [dup *]
|
|
[dup *]
|
|
|
|
|
|
2 F [dupdip over * *] cons
|
|
[dup *] [dupdip over * *] cons
|
|
[[dup *] dupdip over * *]
|
|
|
|
And here it is in action:
|
|
|
|
::
|
|
|
|
2 [[dup *] dupdip over * *] i
|
|
2 [dup *] dupdip over * *
|
|
2 dup * 2 over * *
|
|
2 2 * 2 over * *
|
|
4 2 over * *
|
|
4 2 4 * *
|
|
4 8 *
|
|
32
|
|
|
|
So, it works, but in this case the results of the partial evaluation are
|
|
more elegant.
|
|
|
|
Try it with ``hylomorphism()``:
|
|
-------------------------------
|
|
|
|
.. code:: ipython2
|
|
|
|
def hylomorphism(c, F, P, G):
|
|
'''Return a hylomorphism function H.'''
|
|
|
|
def H(a):
|
|
if P(a):
|
|
result = c
|
|
else:
|
|
b, aa = G(a)
|
|
result = F(b, H(aa))
|
|
return result
|
|
|
|
return H
|
|
|
|
First, curry
|
|
~~~~~~~~~~~~
|
|
|
|
With abuse of syntax:
|
|
|
|
.. code:: ipython2
|
|
|
|
def hylomorphism(c):
|
|
return lambda F: lambda P: lambda G: lambda a: (
|
|
if P(a):
|
|
result = c
|
|
else:
|
|
b, aa = G(a)
|
|
result = F(b)(H(aa))
|
|
return result
|
|
)
|
|
|
|
|
|
lambda lowering
|
|
~~~~~~~~~~~~~~~
|
|
|
|
.. code:: ipython2
|
|
|
|
def hylomorphism(c):
|
|
def r(a):
|
|
def rr(P):
|
|
if P(a):
|
|
return lambda F: lambda G: c
|
|
return lambda F: lambda G: (
|
|
b, aa = G(a)
|
|
return F(b)(H(aa))
|
|
)
|
|
return rr
|
|
return r
|
|
|
|
|
|
|
|
expression lifting
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: ipython2
|
|
|
|
def hylomorphism(c):
|
|
def r(a):
|
|
def rr(P):
|
|
def rrr(G):
|
|
if P(a):
|
|
return lambda F: c
|
|
b, aa = G(a)
|
|
H = hylomorphism(c)(aa)(P)(G)
|
|
return lambda F: F(b)(H(F))
|
|
return rrr
|
|
return rr
|
|
return r
|
|
|
|
|
|
|
|
quoted
|
|
~~~~~~
|
|
|
|
.. code:: ipython2
|
|
|
|
def hylomorphism(c):
|
|
def r(a):
|
|
def rr(P):
|
|
def rrr(G):
|
|
if P(a):
|
|
return "lambda F: %s" % (c,)
|
|
b, aa = G(a)
|
|
H = hylomorphism(c)(aa)(P)(G)
|
|
return "lambda F: F(%(b)s)((%(H)s)(F))" % locals()
|
|
return rrr
|
|
return rr
|
|
return r
|
|
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
hylomorphism(0)(3)(lambda n: n == 0)(lambda n: (n-1, n-1))
|
|
|
|
.. code:: ipython2
|
|
|
|
def F(a):
|
|
def _F(b):
|
|
print a, b
|
|
return a + b
|
|
return _F
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
F(2)(3)
|
|
|
|
.. code:: ipython2
|
|
|
|
eval(hylomorphism(0)(5)(lambda n: n == 0)(lambda n: (n-1, n-1)))(F)
|
|
|
|
.. code:: ipython2
|
|
|
|
eval(hylomorphism([])(5)(lambda n: n == 0)(lambda n: (n-1, n-1)))(lambda a: lambda b: [a] + b)
|
|
|
|
.. code:: ipython2
|
|
|
|
hylomorphism(0)([1, 2, 3])(lambda n: not n)(lambda n: (n[0], n[1:]))
|
|
|
|
.. code:: ipython2
|
|
|
|
hylomorphism([])([1, 2, 3])(lambda n: not n)(lambda n: (n[1:], n[1:]))
|
|
|
|
.. code:: ipython2
|
|
|
|
eval(hylomorphism([])([1, 2, 3])(lambda n: not n)(lambda n: (n[1:], n[1:])))(lambda a: lambda b: [a] + b)
|
|
|
|
.. code:: ipython2
|
|
|
|
def hylomorphism(c):
|
|
return lambda a: lambda P: (
|
|
if P(a):
|
|
result = lambda F: lambda G: c
|
|
else:
|
|
result = lambda F: lambda G: (
|
|
b, aa = G(a)
|
|
return F(b)(H(aa))
|
|
)
|
|
return result
|
|
)
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
def hylomorphism(c):
|
|
return lambda a: (
|
|
lambda F: lambda P: lambda G: c
|
|
if P(a) else
|
|
lambda F: lambda P: lambda G: (
|
|
b, aa = G(a)
|
|
return F(b)(H(aa))
|
|
)
|
|
)
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
def hylomorphism(c):
|
|
return lambda a: lambda G: (
|
|
lambda F: lambda P: c
|
|
if P(a) else
|
|
b, aa = G(a)
|
|
lambda F: lambda P: F(b)(H(aa))
|
|
)
|
|
|