{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "using Symata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Interface with Julia\n", "\n", "This notebook gives examples of Symata interacting with its host language Julia." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Writing and using Julia functions\n", "\n", "It is easy to write elegant, fast, powerful, flexible, Julia code from within Symata.\n", "\n", "As a first example, we multiply powers of elements in two arrays and sum the results. We first try two different methods using pure Symata. Then we write a Julia function to do the same thing.\n", "\n", "Here are the arrays." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "x1 = Range(10.0^3)\n", "y1 = Range(10.0^3);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define a procedural function in Symata to compute the sum." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": true }, "outputs": [], "source": [ "g(x_, y_) := Module([s=0],\n", " begin\n", " For(i=1, i<=Length(x), i += 1, s += x[i]^2 / y[i]^(-3)),\n", " s\n", " end)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Apply this function and time the result." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.069080808,1.6716708333325002e17 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.069080808,1.6716708333325002e17 \\right] $$\"" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(x1,y1)\n", "\n", "resultS1 = Timing(g(x1,y1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In general, it is faster to use mapping and functional methods in Symata.\n", "We can compute the sum like this." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.041056992,7.485470860550343 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.041056992,7.485470860550343 \\right] $$\"" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Apply(Plus, x1^2 / y1^3)\n", "\n", "resultS2 = Timing(Apply(Plus, x1^2 / y1^3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The second method is indeed a bit faster." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ 1.68255891712671 $$" ], "text/plain": [ "L\"$$ 1.68255891712671 $$\"" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resultS1[1]/resultS2[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Writing a Julia function while in Symata mode\n", "\n", "Define a Julia function to do the sum. We choose a functional method." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "jfunc = J((x,y) -> sum(u -> u[1]^2 / u[2]^(3), zip(x,y)));" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.000671538,7.485470860550343 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.000671538,7.485470860550343 \\right] $$\"" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "jfunc(x1,y1)\n", "Timing(jfunc(x1,y1))\n", "\n", "resultJ = Timing(jfunc(x1,y1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Julia function is much faster." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 102.86954424023658,61.138747174396684 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 102.86954424023658,61.138747174396684 \\right] $$\"" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[resultS1[1], resultS2[1] ] / resultJ[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(The Julia function is a factor of 5 times slower here than in Symata 0.3.0-dev.9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will explain later why it is possible to write such simple and fast code that operates on Symata expressions.\n", "\n", "The function `f` also works on symbolic expressions." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\frac{ \\left( a + b \\right) ^{2}}{ \\left( u + v \\right) ^{3}} + \\frac{ \\left( c + d \\right) ^{2}}{ \\left( y + z \\right) ^{3}} $$" ], "text/plain": [ "L\"$$ \\frac{ \\left( a + b \\right) ^{2}}{ \\left( u + v \\right) ^{3}} + \\frac{ \\left( c + d \\right) ^{2}}{ \\left( y + z \\right) ^{3}} $$\"" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "jfunc([a+b, c+d],[u+v,y+z])" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ClearAll(x,a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Evaluating Symata expressions from Julia\n", "\n", "The macro `@sym` evaluates Symata code while in Julia.\n", "\n", "First, we switch to Julia mode." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Julia();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a Symata expression and bind it to the Julia variable `expr`." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ ":a + :b" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr = @sym a + b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are julia functions corresponding to many Symata expression heads." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ ":a^2 + :b^2 + 2:a*:b" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Expand(expr^2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Return to Symata mode" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [], "source": [ "isymata()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calling an existing Julia function\n", "\n", "To define the Julia function, we used the Symata function `J()`. The arguments of `J` are interpreted as pure Julia code, with no translation. It is as if we temporarily enter Julia mode. In fact, we could have defined the function in Julia. Let's try that.\n", "\n", "First, we enter Julia mode." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Julia();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Everything we type will be interpreted as Julia language expressions. We write the Julia function. We will explain later how the function works." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fj(x,y) = sum(u -> u[1]^2 / u[2]^(3), zip(x,y));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Return to Symata mode." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [], "source": [ "isymata();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We set the Symata variable `fj` to the Julia function `fj`. The Julia function was written in the `Main` module. (NB we may change this so Julia functions are evaluated in the Symata module)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.034632529,7.485470860550343 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.034632529,7.485470860550343 \\right] $$\"" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fj = J( Main.fj );\n", "\n", "Timing(fj(x1,y1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use `J()` in this way to call any existing Julia function...\n", "\n", " time()\n", "\n", " Get the system time in seconds since the epoch, with fairly high (typically, \n", " microsecond) resolution.\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 1.532087489793153e9 $$" ], "text/plain": [ "L\"$$ 1.532087489793153e9 $$\"" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "J(time)()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Julia functions for Symata\n", "\n", "How does the Julia function \n", "```\n", "jfunc = (x,y) -> sum(u -> u[1]^2 / u[2]^(3), zip(x,y))\n", "```\n", "work ?\n", "\n", "As in Symata, `x -> body` defines a pure, or anonymous, function. `zip` returns a list of pairs of elements from two lists. In fact, it returns a virtual list, called an *iterator*, which is more efficient. These pairs are supplied sequentially to the to the function, and the results are summed. No intermediate arrays are formed.\n", "\n", "Symata expressions are *iterable objects* in Julia. Most Julia code that operates on iterable objects will work with Symata expressions. `zip` takes two iterable objects and returns an iterable object.\n", "In Julia, Symata expressions are of type `Mxpr`. Notice that we did not write `Mxpr` anywhere in the code. The first time `jfunc` is called with Symata expressions, Julia compiles a method to handle just this case. The compiler is typically very good at writing code optimized for the input type.\n", "\n", "All of this means that the author of Symata wrote no code to implement `zip` or `sum` for Symata expressions.\n", "\n", "In fact `jfunc` can be called with many types objects. To demonstrate this, we perform the sum operation on a Symata list and a Julia Array.\n", "\n", "We set the Symata variable `y2` to a Julia `Array` of 1000 numbers." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 1.0:1.0:1000.0 $$" ], "text/plain": [ "L\"$$ 1.0:1.0:1000.0 $$\"" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y2 = J(range(1, stop=1000.0, length=1000))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that 1000 numbers were not printed. `range` returns a virtual array, that is an iterator. We call `jfunc` twice. The first time, Julia compiles a method for the input types which takes some (not much) time." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.000486682,7.485470860550343 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.000486682,7.485470860550343 \\right] $$\"" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "jfunc(x1,y2)\n", "resultJ2 = Timing(jfunc(x1,y2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that summing over the two types of arrays is a bit slower in this case than using two Symata arrays." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ 1.3798291286712885 $$" ], "text/plain": [ "L\"$$ 1.3798291286712885 $$\"" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resultJ[1]/resultJ2[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we call `jfunc` on two Julia abstract arrays." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.00012182,7.485470860550343 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.00012182,7.485470860550343 \\right] $$\"" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "jfunc(y2,y2)\n", "resultJ3 = Timing(jfunc(y2,y2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Operating on these Julia arrays is about 10 times faster in this case than including a Symata array." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ 3.995091118043014 $$" ], "text/plain": [ "L\"$$ 3.995091118043014 $$\"" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resultJ2[1]/resultJ3[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Compile and SymataCall\n", "\n", "Use `Compile` to compile a Symata expression to Julia" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 9 $$" ], "text/plain": [ "L\"$$ 9 $$\"" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g = Compile([x], x^2);\n", "g(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use `SymataCall` to create a Julia \"callback\" to Symata." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 9 $$" ], "text/plain": [ "L\"$$ 9 $$\"" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g = SymataCall(x, x^2 );\n", "g(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But, `Compile` and `SymataCall` create different functions. `Compile` translates Symata to Julia and compiles the result. `SymataCall` wraps the Symata expression in a Julia function that sets the variable and then Symata-evaluates the result. The function returned by `Compile` executes faster in general. But `SymataCall` works with expressions that cannot be translated and compiled.\n", "\n", "`NIntegrate` effectively uses `SymataCall`. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### @symExpr macro for writing Julia code with Symata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a Symata expression that evaluates to an expression that could be Julia code." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\frac{z}{ \\left( -1 + z \\right) ^{2}} $$" ], "text/plain": [ "L\"$$ \\frac{z}{ \\left( -1 + z \\right) ^{2}} $$\"" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ex1 = Together(PolyLog(-1,z),(1-z))" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\frac{3}{4} $$" ], "text/plain": [ "L\"$$ \\frac{3}{4} $$\"" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ex1 ./ ( z => 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Switch to Julia mode" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Julia()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The macro `@symExpr` evaluates the Symata expression, translates the result to a Julia expression and inserts it into the surrounding Julia expression." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [], "source": [ "? @symExpr" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "f1 (generic function with 1 method)" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f1(z) = @symExpr Together(PolyLog(-1,z),(1-z))" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "3//4" ], "text/plain": [ "3//4" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f1(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function method definition above is equivalent to\n", "```\n", "f1(z) = z/(z-1)^2\n", "```" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [], "source": [ "isymata(); # return to Symata mode" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `Unpack`: Importing Julia arrays into Symata\n", "\n", "Recall the abstract Julia array that we created above" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 1.0:1.0:1000.0 $$" ], "text/plain": [ "L\"$$ 1.0:1.0:1000.0 $$\"" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We import `y2` into Symata using `Unpack`. (`Unpack` currently works only with one dimensional arrays.)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y3 = Unpack(y2);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result is a Symata list. We check that it is indeed a list of the expected length and the first and last elements." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{List},1000,1.0,1000.0 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{List},1000,1.0,1000.0 \\right] $$\"" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[Head(y3), Length(y3), y3[1], y3[-1]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`y3` is equal to `y1`, which was created with Range." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y3 == y1 == Range(10.0^3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`y3` is not a `packed` array, but an ordinary Symata array, a list." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\text{\"cat\"} $$" ], "text/plain": [ "L\"$$ \\text{\\\"cat\\\"} $$\"" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y3[1] = \"cat\"" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{\"cat\"},2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{\\\"cat\\\"},2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0 \\right] $$\"" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y3[1:10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course, we can unpack not just abstract Julia arrays, but physical arrays as well. The Julia function for converting an abstract array to a physical array is `collect`." ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": true }, "outputs": [], "source": [ "y4 = J(collect)(y2);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice what we did here. `J(collect)` gets a Symata reference to the Julia function `collect`. We then call the imported function on the Symata variable `y2`, which refers to an abstract Julia array.\n", "\n", "Now we have a physical Julia array" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{Array{Float64,1}},1.0 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{Array{Float64,1}},1.0 \\right] $$\"" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[Head(y4), y4[1]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also construct a Symata expression directly in Julia, like this." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y5 = J(mxpr(:List, Any[collect(range(1.0, stop=1000, length=1000))...]));" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{List},1000.0 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{List},1000.0 \\right] $$\"" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[Head(y5), y5[-1]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `Pack`: Converting Symata arrays to Julia arrays.\n", "\n", "\n", "Recall that `y1` and `y3` are both Symata lists." ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{List},\\text{List} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{List},\\text{List} \\right] $$\"" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[Head(y1), Head(y3)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We pack these lists to Julia arrays like this." ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "collapsed": true }, "outputs": [], "source": [ "y6 = Pack(y1)\n", "y7 = Pack(y3);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What type of object was created ?" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{Array{Float64,1}},\\text{Array{Any,1}} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{Array{Float64,1}},\\text{Array{Any,1}} \\right] $$\"" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[Head(y6), Head(y7)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first array is of type `Float64` and the second of type `Any`. They are different because, while all elements of `y1` are floating point numbers, we set the first element of `y7` to `\"cat\"`. When copying arrays, Julia creates a container of the most specific type that will contain all elements." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using Symata in Julia code" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### *NOTE* Julia-mode help is currently broken (in both IJulia, and the Symata-IJulia-Julia mode)\n", " Symata-mode help does work." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Switch from Symata to Julia mode" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Julia();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will use the following functions and macros" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "collapsed": false }, "outputs": [], "source": [ "? @sym" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "search: \u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22m\u001b[0m\u001b[1mp\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1mt\u001b[22m\u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1mi\u001b[22m\u001b[0m\u001b[1mn\u001b[22m\u001b[0m\u001b[1mg\u001b[22m\n", "\n" ] } ], "source": [ "? symparsestring" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "search: \u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1mv\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1ml\u001b[22m \u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22mpars\u001b[0m\u001b[1me\u001b[22me\u001b[0m\u001b[1mv\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1ml\u001b[22m \u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22mtrans\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1mv\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1ml\u001b[22m\n", "\n" ] } ], "source": [ "? symeval" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "search: \u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22m\u001b[0m\u001b[1mt\u001b[22m\u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1mn\u001b[22m\u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1mv\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1ml\u001b[22m\n", "\n" ] } ], "source": [ "? symtranseval" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "search: \u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22m\u001b[0m\u001b[1mp\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1mv\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1ml\u001b[22m\n", "\n" ] } ], "source": [ "? symparseeval" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "search: \u001b[0m\u001b[1mg\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1mt\u001b[22m\u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1mt\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\n", "\n" ] } ], "source": [ "? getsymata" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "search: \u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1mt\u001b[22m\u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22m\u001b[0m\u001b[1ma\u001b[22m\u001b[0m\u001b[1mt\u001b[22m\u001b[0m\u001b[1ma\u001b[22m \u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1mt\u001b[22m\u001b[0m\u001b[1ms\u001b[22m\u001b[0m\u001b[1my\u001b[22m\u001b[0m\u001b[1mm\u001b[22mv\u001b[0m\u001b[1ma\u001b[22ml\n", "\n" ] } ], "source": [ "? setsymata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The macro `@sym` interprets and evaluates its argument in Symata." ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a in Julia is 1\n", "a in Symata is 3\n" ] } ], "source": [ "a = 1 # set a to 1 in Julia\n", "@sym a = 3 # set a to 3 in Symata\n", "println(\"a in Julia is $a\") # print a in Julia\n", "@sym Println(\"a in Symata is $a\") # print a in Symata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can assign a value in Symata by using `@sym`" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\"cat\"" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@sym z = \"cat\"\n", "@sym z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But `@sym` will not work inside a function. Use `getsymata` and `setsymata` instead" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\"cat\"" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "getsymata(:z)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "\"dog\"" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "setsymata(:z, \"dog\")\n", "getsymata(:z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`unpacktoList` converts a Julia array to a Symat list. " ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "[1.0,2.5,4.0]" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "unpacktoList(range(1, stop=4, length=3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parse a string of Symata code" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ ":(Sqrt(a))" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "scode = Meta.parse(\"Sqrt(a)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is a valid Julia expression, although evaluating it in Julia may cause an error. We translate the expression to Symata and send it through the Symata evaluation sequence." ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3^(1//2)" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "res = symtranseval(scode)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Print this as Symata would" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3^(1/2)\n" ] } ], "source": [ "symprintln(res)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Put this together in a function" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1,2^(1/2),3^(1/2),2,5^(1/2),2^(1/2)*3^(1/2),7^(1/2),2*2^(1/2),3]\n" ] } ], "source": [ "function squareroots()\n", " a = 1:9\n", " setsymata(:a, unpacktoList(a))\n", " symprintln(symparseeval(\"Sqrt(a)\"))\n", " nothing\n", "end\n", "\n", "squareroots()" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "collapsed": false }, "outputs": [], "source": [ "isymata() # return to Symata mode" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can translate Symata to Julia like this" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\text{\"3 * x ^ 2 * y ^ 3 + cos(1)\"} $$" ], "text/plain": [ "L\"$$ \\text{\\\"3 * x ^ 2 * y ^ 3 + cos(1)\\\"} $$\"" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ToJuliaString(3 * x^2 * y^3 + Cos(1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following gives code that will only work when the Symata module is loaded." ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\text{\"mplus(mmul(3, mpow(x, 2), mpow(y, 3)), Cos(1))\"} $$" ], "text/plain": [ "L\"$$ \\text{\\\"mplus(mmul(3, mpow(x, 2), mpow(y, 3)), Cos(1))\\\"} $$\"" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s2 = ToJuliaString( 3*x^2*y^3 + Cos(1), NoSymata => False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can parse and evaluate this string in Julia." ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 216 + \\text{Cos} \\! \\left( 1 \\right) $$" ], "text/plain": [ "L\"$$ 216 + \\text{Cos} \\! \\left( 1 \\right) $$\"" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ExportJ(s2);\n", "J(x = 3, y = 2);\n", "J(eval(Meta.parse(Main.s2)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Symata expressions are of type `Mxpr`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We assigned a value to `a` in symata" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Julia()" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5,6,7,8,9]" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = getsymata(:a)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Mxpr{:List}" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`mhead` returns the head of a symata expression." ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ ":List" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mhead(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`margs` returns the arguments of a Symata expression." ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "9-element Array{Any,1}:\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5\n", " 6\n", " 7\n", " 8\n", " 9" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "margs(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Use `mxpr` to construct a Symata expression" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Cos(:Pi*2)" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ex = mxpr(:Cos, mxpr(:Times, :Pi, 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`mxpr` creates an object but does not evaluate it. Evaluate `ex` with `symeval`." ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symeval(ex)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It may be more efficient to create a Symata expression by first filling an array of arguments" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = newargs(5);" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "5-element Array{Any,1}:\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "copyto!(a, 1:5)" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5]" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mxpra(:List,a)" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1 + 2 + 3 + 4 + 5" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mxpra(:Plus,a)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "15" ], "text/plain": [ "15" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symeval(mxpra(:Plus,a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of `mxpr`, we used `mxpra`, which does not copy the array of arguments." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can create Symata expressions like this" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6, :b*:a\n" ] } ], "source": [ "println(mmul(3,2), \", \" , mmul(:b, :a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`mmul`, `mpow`, `mplus`, `mminus` are arithemtic methods that can create Symata expressions. When called inside a function with numerical arguments, the compiler will replace them with an efficent Julia method.\n", "\n", "We verify this by inspectin the lowered code." ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\t.text\n", "; Function mmul {\n", "; Location: arithmetic.jl:60\n", "; Function *; {\n", "; Location: julia_level.jl:155\n", "; Function *; {\n", "; Location: arithmetic.jl:60\n", "\timulq\t%rsi, %rdi\n", ";}}\n", "\tmovq\t%rdi, %rax\n", "\tretq\n", "\tnopl\t(%rax,%rax)\n", ";}\n" ] } ], "source": [ "import InteractiveUtils \n", "InteractiveUtils.code_native(mmul,(Int,Int))" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\t.text\n", "; Function * {\n", "; Location: int.jl:54\n", "\timulq\t%rsi, %rdi\n", "\tmovq\t%rdi, %rax\n", "\tretq\n", "\tnopl\t(%rax,%rax)\n", ";}\n" ] } ], "source": [ "InteractiveUtils.code_native(*, (Int,Int))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Many Symata functions have equivalents in Julia. These functions both construct Symata expressions and evaluate them." ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Cos(mmul(2,Pi))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`symmatamath()` defines methods allowing you to use `*` for `mmul`, etc." ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "collapsed": false }, "outputs": [], "source": [ "symatamath()" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Cos(2Pi)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that `Cos` is replaced by efficient Julia methods when possible." ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "collapsed": false }, "outputs": [], "source": [ "isymata()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Version and date" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symata version 0.4.1-dev.3\n", "Julia version 0.7.0-beta2.1\n", "Python version 2.7.14+\n", "SymPy version 1.0\n" ] } ], "source": [ "VersionInfo()" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2018-07-20T13:51:52.167" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "InputForm(Now())" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 0.7.0-beta2", "language": "julia", "name": "julia-0.7" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "0.7.0" } }, "nbformat": 4, "nbformat_minor": 1 }