{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to Symata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Load Symata like this" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "using Symata;" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Symata is a computer language for symbolic mathematics written in Julia. After typing `using Symata`, you are in Symata mode and input is interpreted as Symata language expressions.\n", "\n", "Here is a Symata expression" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\left( x + y \\right) ^{3} $$" ], "text/plain": [ "L\"$$ \\left( x + y \\right) ^{3} $$\"" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(x+y)^3" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "In a Jupyter notebook, you can exit Symata mode and enter Julia mode by entering `Julia()`. Expressions are then Julia language expressions." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "Julia()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "length(zeros(10)) == 10 # A Julia expression" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In Jupyter, type `isymata()` to leave Julia mode and enter `Symata` mode. At the command line REPL, type `=` at the beginning of a line to enter `Symata` mode." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "isymata() # we enter Symata mode again" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note**: to leave `Symata` mode and return to `Julia` mode, type `Julia()` in `IJulia`, or backspace at the command line REPL.\n", "\n", "Now we can enter `Symata` expressions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Entering expressions\n", "\n", "You enter expressions and Symata evaluates them" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{Cos} \\! \\left( \\pi \\ x \\right) $$" ], "text/plain": [ "L\"$$ \\text{Cos} \\! \\left( \\pi \\ x \\right) $$\"" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr = Cos(π * x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A variable is set like this." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\frac{1}{3} $$" ], "text/plain": [ "L\"$$ \\frac{1}{3} $$\"" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 1/3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the cosine is evaluated" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\frac{1}{2} $$" ], "text/plain": [ "L\"$$ \\frac{1}{2} $$\"" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\frac{1}{6} $$" ], "text/plain": [ "L\"$$ \\frac{1}{6} $$\"" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 1/6" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\frac{3^{\\frac{1}{2}}}{2} $$" ], "text/plain": [ "L\"$$ \\frac{3^{\\frac{1}{2}}}{2} $$\"" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Clear the value of `x` and the cosine can no longer be reduced." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{Cos} \\! \\left( \\pi \\ x \\right) $$" ], "text/plain": [ "L\"$$ \\text{Cos} \\! \\left( \\pi \\ x \\right) $$\"" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Clear(x)\n", "\n", "expr" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is clear what happened above. If `x` is not set to a value, then `Cos(π * x)` can't be written in a simpler form. If we set `x` to some particular values, then `Cos(π * x)` *can* be reduced to a simpler form.\n", "\n", "*(you can skip the following the first time through)*\n", "\n", "- But, the reason `Symata` understands this is a consquence of the procedure it follows in evaluating (almost) all expressions. `Symata` evaluates expressions to a fixed point. More precisely, when an expression is given as input, `Symata` descends depth-first evaluating each subexpression to a fixed point and finally the top-level expression to a fixed point. When `Cos(π * x)` is first evaluated, each of `π` and `x` evaluates to itself so that `π * x` is already at a fixed point. Since there is no rule for evaluating `Cos(π * x)` for fixed `π * x`, `Cos(π * x)` is also at a fixed point.\n", "\n", "- The expression `x=1/3` means that, whenever `x` is encountered, it should evaluate to `1/3`. The expression `Out(4)` evaluates to the fourth output cell, which is `Cos(π * x)`. Then `π` evaluates to iteself, `x` evaluates to `1`, so that `π * x` evaluates to `π/3`. There is a rule saying that `Cos(π/3)` evaluates to `1/2`.\n", "\n", "- `Clear(x)` says that `x` should once again evaluate to itself. Then evaluating `Out(4)` follows the same evaluation sequence, leading to `Cos(π * x)`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Assigning values\n", "\n", "There are several kinds of assignment in Symata. The two most common are `=` (or `Set`) and `:=` (or `SetDelayed`).\n", "`Set` immediatley evaluates the right hand side and binds the left hand side to the result. `SetDelayed` does not evaluate the right hand side when the assignment is made. It evaluates the right hand side every time the left hand side is subsequently evalutated and then binds the result.\n", "\n", "The following demonstrates the difference." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "x = 1\n", "a := x\n", "b = x\n", "c = a\n", "d := a" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 1,1,1,1,1 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 1,1,1,1,1 \\right] $$\"" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x,a,b,c,d]" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "ClearAll(x)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ x,x,1,1,x \\right] $$" ], "text/plain": [ "L\"$$ \\left[ x,x,1,1,x \\right] $$\"" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x,a,b,c,d]" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ x,z,1,1,z \\right] $$" ], "text/plain": [ "L\"$$ \\left[ x,z,1,1,z \\right] $$\"" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(a = z, [x,a,b,c,d])" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "ClearAll(x,a,b,c,d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Destructuring assignment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Assign two variables at once" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ x,y \\right] $$" ], "text/plain": [ "L\"$$ \\left[ x,y \\right] $$\"" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[a,b] = [x,y]" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ x $$" ], "text/plain": [ "L\"$$ x $$\"" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ y $$" ], "text/plain": [ "L\"$$ y $$\"" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ x,y \\right] $$" ], "text/plain": [ "L\"$$ \\left[ x,y \\right] $$\"" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[a,b]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Swap two values" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ y,x \\right] $$" ], "text/plain": [ "L\"$$ \\left[ y,x \\right] $$\"" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[a,b] = [b,a]" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ y,x \\right] $$" ], "text/plain": [ "L\"$$ \\left[ y,x \\right] $$\"" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[a,b]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Expressions and parts of expressions\n", "\n", "*Expression* is the central concept in Symata. In general, expressions are trees whose branches and leaves are other expressions. You can manipulate these expressions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every expression has a `Head`. For function-like expressions, the `Head` is the function name. For atomic expressions, the `Head` usually is a data type." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{Symbol},\\text{Plus},\\text{List},\\text{Cos},f,\\text{Int64},\\text{Float64},\\text{BigInt},\\text{BigFloat} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{Symbol},\\text{Plus},\\text{List},\\text{Cos},f,\\text{Int64},\\text{Float64},\\text{BigInt},\\text{BigFloat} \\right] $$\"" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Map(Head, [x, x + y, [x,y], Cos(x), f(x), 3, 3.0, BI(3), BF(3)])" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/latex": [ "$$ x^{3} + 3 \\ x^{2} \\ y + 3 \\ x \\ y^{2} + y^{3} $$" ], "text/plain": [ "L\"$$ x^{3} + 3 \\ x^{2} \\ y + 3 \\ x \\ y^{2} + y^{3} $$\"" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr = Expand((x+y)^3)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Plus(Power(x,3),Times(3,Power(x,2),y),Times(3,x,Power(y,2)),Power(y,3))" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "FullForm(expr) # This shows the internal form. The tree is explicit" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ x $$" ], "text/plain": [ "L\"$$ x $$\"" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr[2,2,1] # Return a part of the expression by index into the tree" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "scrolled": true }, "outputs": [], "source": [ "expr[2,2,1] = z; # Replace a part of the expression" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ x^{3} + 3 \\ z^{2} \\ y + 3 \\ x \\ y^{2} + y^{3} $$" ], "text/plain": [ "L\"$$ x^{3} + 3 \\ z^{2} \\ y + 3 \\ x \\ y^{2} + y^{3} $$\"" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ z $$" ], "text/plain": [ "L\"$$ z $$\"" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Part(expr,2,2,1) # You can do the same thing with Part" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ y $$" ], "text/plain": [ "L\"$$ y $$\"" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Expand((x+y)^3)[4,1] # You can get parts of expressions directly" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "expr = Expand((x+y)^20); # The semi-colon suppresses printing the return value." ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 77520 \\ x^{7} \\ y^{13} + 15504 \\ x^{5} \\ y^{15} + 1140 \\ x^{3} \\ y^{17} $$" ], "text/plain": [ "L\"$$ 77520 \\ x^{7} \\ y^{13} + 15504 \\ x^{5} \\ y^{15} + 1140 \\ x^{3} \\ y^{17} $$\"" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr[14:18:2] # Parts 14 through 18 with step 2" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "ClearAll(expr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define a function that collects an expression's head and arguments in a list." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "headargs(f_(args__)) := [f,args]" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{Plus},3,x^{2},y \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{Plus},3,x^{2},y \\right] $$\"" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "headargs(a + b^2 + 3)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\int f \\! \\left( x \\right) \\, \\mathbb{d}x $$" ], "text/plain": [ "L\"$$ \\int f \\! \\left( x \\right) \\, \\mathbb{d}x $$\"" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Integrate(f(x),x)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{Integrate},f \\! \\left( x \\right) , \\left[ x \\right] \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{Integrate},f \\! \\left( x \\right) , \\left[ x \\right] \\right] $$\"" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "headargs(Integrate(f(x),x))" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "ClearAll(a,b,c,d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Rotate the head and arguments to make a new expression." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "rotheadargs(f_(args__)) := (Last([args])(f,Splat(Most([args]))))" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ d \\! \\left( \\text{Plus},a,b,c \\right) $$" ], "text/plain": [ "L\"$$ d \\! \\left( \\text{Plus},a,b,c \\right) $$\"" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rotheadargs( a + b + c + d)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ g \\! \\left( x \\right) \\! \\left( \\text{Plus},a,b,c,d \\right) $$" ], "text/plain": [ "L\"$$ g \\! \\left( x \\right) \\! \\left( \\text{Plus},a,b,c,d \\right) $$\"" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rotheadargs( a + b + c + d + g(x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Definition\n", "\n", "`Definition` shows the definitions associated with a symbol" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "ClearAll(x,a,b,c,d) # delete definitions from the previous example" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 1 $$" ], "text/plain": [ "L\"$$ 1 $$\"" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 1" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a=1\n", "\n", "\n" ] } ], "source": [ "Definition(a)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a:=x\n", "\n", "\n" ] } ], "source": [ "a := x\n", "\n", "Definition(a) # This overwrites the previous definition" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "f(x_):=x^2\n", "f(x_,y_):=(x + y)\n", "\n", "\n" ] } ], "source": [ "f(x_) := x^2\n", "f(x_, y_) := x + y\n", "Definition(f)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "f(x_):=x^2\n", "f(x_,y_):=(x + y)\n", "\n", "\n" ] } ], "source": [ "Definition(f)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "ClearAll(f,a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cpu time, memory, and tracing the evaluation\n" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.301667046,\\text{Null} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.301667046,\\text{Null} \\right] $$\"" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Timing((Range(10^6), Null )) # time a single expression" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Time` toggles the timing of every expression entered. Memory allocated and the number of attempts to apply a user defined rule are also printed." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{False} $$" ], "text/plain": [ "L\"$$ \\text{False} $$\"" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Time(True) # Enable timing all evaluation. Returns the previous value" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.007442 seconds (999.54 k allocations: 22.885 MiB)\n", "tryrule count: downvalue 0, upvalue 0\n" ] } ], "source": [ "Range(10^6);" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.000038 seconds (25 allocations: 2.422 KiB)\n", "tryrule count: downvalue 0, upvalue 0\n" ] } ], "source": [ "Time(False); # disable timing all evaluation." ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2<< False\n", "1<< False\n" ] } ], "source": [ "Trace(True); # Trace evaluation" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">>1 CompoundExpression((a + b)*(a + b))\n", " >>2 (a + b)*(a + b)\n", " >>3 a + b\n", " 3<< a + b\n", " >>3 a + b\n", " 3<< a + b\n", " 2<< (a + b)^(1 + 1)\n", " >>2 (a + b)^(1 + 1)\n", " >>3 1 + 1\n", " 3<< 2\n", " 2<< (a + b)^2\n", " >>2 (a + b)^2\n", " 2<< (a + b)^2\n", "1<< (a + b)^2\n", ">>1 (a + b)^2\n", "1<< (a + b)^2\n" ] }, { "data": { "text/latex": [ "$$ \\left( a + b \\right) ^{2} $$" ], "text/plain": [ "L\"$$ \\left( a + b \\right) ^{2} $$\"" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(a+b)*(a+b)" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">>1 CompoundExpression(Trace(False))\n", " >>2 Trace(False)\n" ] } ], "source": [ "Trace(False);" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "\\begin{verbatim}\n", "LeafCount(expr)\n", "\\end{verbatim}\n", "gives the number of indivisible (\\texttt{Part} can't be taken) elements in \\texttt{expr}.\n", "\n", "This amounts to counting all the \\texttt{Heads} and all of the arguments that are not of type \\texttt{Mxpr}, that is compound expressions.\n", "\n", "A more accurate name is \\texttt{NodeCount}.\n", "\n" ], "text/markdown": [ "```\n", "LeafCount(expr)\n", "```\n", "\n", "gives the number of indivisible (`Part` can't be taken) elements in `expr`.\n", "\n", "This amounts to counting all the `Heads` and all of the arguments that are not of type `Mxpr`, that is compound expressions.\n", "\n", "A more accurate name is `NodeCount`.\n" ], "text/plain": [ "\u001b[36m LeafCount(expr)\u001b[39m\n", "\n", " gives the number of indivisible (\u001b[36mPart\u001b[39m can't be taken) elements in \u001b[36mexpr\u001b[39m.\n", "\n", " This amounts to counting all the \u001b[36mHeads\u001b[39m and all of the arguments that are not\n", " of type \u001b[36mMxpr\u001b[39m, that is compound expressions.\n", "\n", " A more accurate name is \u001b[36mNodeCount\u001b[39m." ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", " Attributes(LeafCount) = [Protected]\n" ] } ], "source": [ "? LeafCount" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 19 $$" ], "text/plain": [ "L\"$$ 19 $$\"" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "LeafCount(Expand((a+b)^3))" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 446 $$" ], "text/plain": [ "L\"$$ 446 $$\"" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ByteCount(Expand((a+b)^3))" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "\\begin{verbatim}\n", "Depth(expr)\n", "\\end{verbatim}\n", "gives the maximum number of indices required to specify any part of \\texttt{expr}, plus \\texttt{1}.\n", "\n" ], "text/markdown": [ "```\n", "Depth(expr)\n", "```\n", "\n", "gives the maximum number of indices required to specify any part of `expr`, plus `1`.\n" ], "text/plain": [ "\u001b[36m Depth(expr)\u001b[39m\n", "\n", " gives the maximum number of indices required to specify any part of \u001b[36mexpr\u001b[39m,\n", " plus \u001b[36m1\u001b[39m." ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", " Attributes(Depth) = [Protected]\n" ] } ], "source": [ "? Depth" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 4 $$" ], "text/plain": [ "L\"$$ 4 $$\"" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Depth(Expand((a+b)^3))" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Plus(Power(a,3),Times(3,Power(a,2),b),Times(3,a,Power(b,2)),Power(b,3))" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "FullForm(Expand((a+b)^3)) # Examine the tree" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ a $$" ], "text/plain": [ "L\"$$ a $$\"" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Expand((a+b)^3)[2,2,1] # One of the deepest parts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calculations using expressions or functions\n", "\n", "Here are a few ways to compute an integral" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{ArcTan} \\! \\left( x \\right) $$" ], "text/plain": [ "L\"$$ \\text{ArcTan} \\! \\left( x \\right) $$\"" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Integrate( (1+x^2)^(-1), x)" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{ArcTan} \\! \\left( x \\right) $$" ], "text/plain": [ "L\"$$ \\text{ArcTan} \\! \\left( x \\right) $$\"" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr = 1/(1+x^2)\n", "Integrate(expr, x)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{ArcTan} \\! \\left( x \\right) $$" ], "text/plain": [ "L\"$$ \\text{ArcTan} \\! \\left( x \\right) $$\"" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(x_) := 1/(1+x^2)\n", "Integrate(expr, x)" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left( 1 + x^{2} \\right) ^{-1} $$" ], "text/plain": [ "L\"$$ \\left( 1 + x^{2} \\right) ^{-1} $$\"" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(x_) = expr # Note we do not use \":=\"" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "ClearAll(expr) # We did not use SetDelay, so we can delete expr" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{ArcTan} \\! \\left( y \\right) $$" ], "text/plain": [ "L\"$$ \\text{ArcTan} \\! \\left( y \\right) $$\"" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Integrate(g(y),y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: Trying to use a compiled (Julia) function `h = J( x -> 1/(1+x^2))` will *not* work." ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [], "source": [ "ClearAll(f,g,expr)\n", "\n", "# Integrate(f(y), y) # The integral can no longer be reduced" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Patterns and Matching\n", "\n", "A `Blank` with no constraints matches everything" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MatchQ(z,_) # Blank matchs z" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{True},\\text{True},\\text{True},\\text{True} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{True},\\text{True},\\text{True},\\text{True} \\right] $$\"" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Map(MatchQ(_), [1,\"string\", a+b, 1/3]) # MatchQ does Currying with the first argument" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`_head` is a `Blank` that only matches expressions with `Head` equal to `head`. " ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Blank(Integer)" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "FullForm(_Integer) # underscore is shorthand for Blank" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MatchQ(1, _Integer)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use Currying to define a predicate function" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [], "source": [ "myintq = MatchQ(_Integer);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not all rational numbers are integers" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{False},\\text{True},\\text{False},\\text{True},\\text{False},\\text{True},\\text{False},\\text{True},\\text{False},\\text{True} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{False},\\text{True},\\text{False},\\text{True},\\text{False},\\text{True},\\text{False},\\text{True},\\text{False},\\text{True} \\right] $$\"" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Map(myintq, Range(1/2,5,1/2))" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MatchQ(b^2, _^2) # Match power with exponent equal to 2" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MatchQ(b^3, _^_) # Match any power" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MatchQ((b+c)^3, _^_)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\text{False} $$" ], "text/plain": [ "L\"$$ \\text{False} $$\"" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MatchQ(b^1, _^_)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This failed because `b^1` evaluates to `b`, which does not have the structure of a power" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The pattern can be complex with blanks deep in an expression." ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{True},\\text{False},\\text{False} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{True},\\text{False},\\text{False} \\right] $$\"" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Map(MatchQ(f(x_^2)), [f(b^2), f(b^3), g(b^2)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Specify a \"function\" `Head` that must match" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{True},\\text{True},\\text{False} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{True},\\text{True},\\text{False} \\right] $$\"" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Map(MatchQ(_gg), [gg(x+y), gg(x), g(x)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define a predicate for positive integers" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{True},\\text{True},\\text{False},\\text{False},\\text{False},\\text{False} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{True},\\text{True},\\text{False},\\text{False},\\text{False},\\text{False} \\right] $$\"" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = MatchQ(_Integer`Positive`)\n", "\n", "Map(m, [1,100, 0, -1, 1.0, x])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also put a `Condition` on a `Pattern`. This matches pairs with the first element smaller than the second." ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{False},\\text{True},\\text{False},\\text{False} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{False},\\text{True},\\text{False},\\text{False} \\right] $$\"" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = MatchQ(Condition([x_, y_], x < y))\n", "\n", "Map(m, [[2, 1], [1, 2], [1,2,3], 1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Pattern`s can include `Alternative`s." ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\text{True},\\text{True},\\text{False} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\text{True},\\text{True},\\text{False} \\right] $$\"" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = MatchQ(_Integer | _String)\n", "\n", "Map(m, [1, \"zebra\", 1.0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Repeated(expr)` matches one or more occurences of `expr`." ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MatchQ([a,a,a,b], [Repeated(a),b])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`RepeatedNull` matches zero or more occurences." ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MatchQ([b], [RepeatedNull(a),b])" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [], "source": [ "ClearAll(m)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Replacing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Rule`s are used for many things in Symata, including replacement. Replacement is a key ingredient in the implementation of functions.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When applied, this `Rule` matches and does a replacement on any expression with `Head` `f` and a `List` of two elements as the sole argument." ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ f \\! \\left( \\left[ x\\text{_},y\\text{_} \\right] \\right) \\Rightarrow p \\! \\left( x + y \\right) $$" ], "text/plain": [ "L\"$$ f \\! \\left( \\left[ x\\text{_},y\\text{_} \\right] \\right) \\Rightarrow p \\! \\left( x + y \\right) $$\"" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f([x_, y_]) => p(x + y)" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "scrolled": true }, "outputs": [], "source": [ "expr = f([x + y, y]) + f(c) + g([a, b]);" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ f \\! \\left( c \\right) + g \\! \\left( \\left[ a,b \\right] \\right) + p \\! \\left( x + 2 \\ y \\right) $$" ], "text/plain": [ "L\"$$ f \\! \\left( c \\right) + g \\! \\left( \\left[ a,b \\right] \\right) + p \\! \\left( x + 2 \\ y \\right) $$\"" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ReplaceAll(expr, f([x_, y_]) => p(x + y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are several things to note here. \n", "\n", "- The pattern `x_` puts no restrictions on the match; any expression will match. The name of the pattern `x` only serves to identify it later during a replacement. \n", "\n", "- Here `x_` has matched `x+y`, but these two uses of `x` are not confused in the result. That is, in `x_`, the symbol `x` is a dummy variable. \n", "\n", "- The expression `f(c)` has a matching `Head`, but not matching arguments, so `f(c)` fails to match. Likewise, the expression `g([a,b])` has matching arguments, but not matching head.\n", "\n", "- The expression `f([x+y,y])` matches, and the replacement is made in (a copy of) `expr`. But, Symata alays evaluates expressions to a fixed point. So `y+y` is replaced by `2y`, and the terms in `expr` are rearranged into the canonical order." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, be aware that matching is structural." ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ a \\ d \\! \\left( -1 \\right) ,d \\! \\left( -2 \\right) ,2 \\ d \\! \\left( -2 \\right) \\right] $$" ], "text/plain": [ "L\"$$ \\left[ a \\ d \\! \\left( -1 \\right) ,d \\! \\left( -2 \\right) ,2 \\ d \\! \\left( -2 \\right) \\right] $$\"" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ReplaceAll([a/b, 1/b^2, 2/b^2] , b^n_ => d(n))" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [], "source": [ "ClearAll(expr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Named patterns that appear in more than one place must match the same expression." ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ b,a, \\left[ a,b \\right] \\right] $$" ], "text/plain": [ "L\"$$ \\left[ b,a, \\left[ a,b \\right] \\right] $$\"" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ReplaceAll([b, a, [a, b]], [x_, y_, [x_, y_]] => 1 ) # This does not match" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 1 $$" ], "text/plain": [ "L\"$$ 1 $$\"" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ReplaceAll([a, b, [a, b]] , [x_, y_, [x_, y_]] => 1 ) # This does match" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example uses `Alternative`s." ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ x,x,c,d,x,x,x,x \\right] $$" ], "text/plain": [ "L\"$$ \\left[ x,x,c,d,x,x,x,x \\right] $$\"" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ " ReplaceAll( [a, b, c, d, a, b, b, b], a | b => x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The arguments of `Sequence` are spliced into expressions during evaluation." ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 1,2,a,b \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 1,2,a,b \\right] $$\"" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[1,2,Sequence(a,b)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An unmatched alternative is replaced by `Sequence()`. Upon evaluation to fixed point, this empty sequence is removed." ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [], "source": [ "f(x_, x_ | y_String) := [x,y]" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 2 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 2 \\right] $$\"" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(2,2) # `y` does not match, so it is removed." ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 2,\\text{\"cat\"} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 2,\\text{\\\"cat\\\"} \\right] $$\"" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(2,\"cat\") # Here the second Alternative matches" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ f \\! \\left( 2,3 \\right) $$" ], "text/plain": [ "L\"$$ f \\! \\left( 2,3 \\right) $$\"" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(2,3) # Here the Pattern fails to match." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Alternative`s, and `Pattern`s in general, can be explicit expressions, with no `Blank`s." ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ p,p,h \\! \\left( c \\right) ,h \\! \\left( d \\right) \\right] $$" ], "text/plain": [ "L\"$$ \\left[ p,p,h \\! \\left( c \\right) ,h \\! \\left( d \\right) \\right] $$\"" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "h(a | b) := p\n", "[h(a), h(b), h(c), h(d)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`ReplaceAll` replaces all matching subexpressions. We can also specify the levels. This matches at level 2 and deeper." ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 1 + b + f \\! \\left( b \\right) + g \\! \\left( f \\! \\left( a \\right) \\right) $$" ], "text/plain": [ "L\"$$ 1 + b + f \\! \\left( b \\right) + g \\! \\left( f \\! \\left( a \\right) \\right) $$\"" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ " Replace(1 + a + f(a) + g(f(a)), a => b, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This replaces only at level 2." ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\text{True} $$" ], "text/plain": [ "L\"$$ \\text{True} $$\"" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ " Replace(1 + a + f(a) + g(f(a)), a => b, [2]) == 1 + a + f(b) + g(f(a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Rule` evaluates the right hand side once, when it is first evaluated." ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.7868225577202423,0.7868225577202423,0.7868225577202423,0.7868225577202423,0.7868225577202423 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.7868225577202423,0.7868225577202423,0.7868225577202423,0.7868225577202423,0.7868225577202423 \\right] $$\"" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ReplaceAll([x, x, x, x, x], x => RandomReal() )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`RuleDelayed` evaluates the right hand side every time it is applie" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.7787894082419318,0.1563184973578402,0.9805473095756001,0.806014248829132,0.5284961636679568 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.7787894082419318,0.1563184973578402,0.9805473095756001,0.806014248829132,0.5284961636679568 \\right] $$\"" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ReplaceAll([x, x, x, x, x], RuleDelayed(x ,RandomReal()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Except` matches everything except expressions that match its argument. The following applies the replacement at level 1." ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [], "source": [ "# FIXME: This example is broken.\n", "Replace([1, 7, \"Hi\", 3, Indeterminate], Except(_`Numeric`) => 0, 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each `Rule` in a `List` of `Rule`s is tried in turn. Matching stops after the first match. `ReplaceRepeated` continues applying `Rules` until the expression reaches a fixed point." ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 25 + y^{6} $$" ], "text/plain": [ "L\"$$ 25 + y^{6} $$\"" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ " ReplaceRepeated(x^2 + y^6 , List(x => 2 + a, a => 3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Up to this point, we have used named patterns only with a single blank, for example `b_`. But, we may associate a name with any pattern expression, including a complex (compound) expression." ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ g \\! \\left( b^{c} \\right) $$" ], "text/plain": [ "L\"$$ g \\! \\left( b^{c} \\right) $$\"" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ " ReplaceAll( b^c, a::(_^_) => g(a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Patterns` are used to implement optional arguments." ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ a + b + z,3 + a + b \\right] $$" ], "text/plain": [ "L\"$$ \\left[ a + b + z,3 + a + b \\right] $$\"" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(x_, y_:3) := x + y\n", "\n", "[f(a+b,z), f(a+b)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Condition` may be used in definitions like this:" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [], "source": [ "ClearAll(f)\n", "\n", "f(x_) := Condition(x^2, x > 3)" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 16,f \\! \\left( 3 \\right) \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 16,f \\! \\left( 3 \\right) \\right] $$\"" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[f(4),f(3)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can match and replace with a `Pattern` with `Head` equal to `Plus`" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ b \\ y \\ z $$" ], "text/plain": [ "L\"$$ b \\ y \\ z $$\"" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ReplaceAll(z * y + b , x_ + y_ => x * y)" ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ b + c + y \\ z $$" ], "text/plain": [ "L\"$$ b + c + y \\ z $$\"" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ReplaceAll(z * y + b + c, x_ + y_ => x * y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This failed because `Plus` with two terms does not match `Plus` with three terms. But, we actually *do* want this to match. Implementing associative-commuatative matching is a major outstanding problem in Symata. Anyone want to give it a try ?" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [], "source": [ "ClearAll(f,h,a,b,x,y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Interface to compiled languages\n", "\n", "Symata's host language is Julia, a high-performance, compiled language. It can be useful to call Juila code from Symata or to compile Symata code to Julia. Symata is also an open-source project, which means you can alter or add to it directly." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define a compiled function to a built-in or user-defined Julia function like this" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calling compiled functions" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 1.0 $$" ], "text/plain": [ "L\"$$ 1.0 $$\"" ] }, "execution_count": 116, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mylog = J(log )\n", "\n", "mylog(2, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also easily write compiled code like this" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ 25.0 $$" ], "text/plain": [ "L\"$$ 25.0 $$\"" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = J((x,y) -> x^2 + y^2)\n", "\n", "f(3.0,4.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we did not specify the data types. Is this really high-performance compiled code ? Yes it is. The function was compiled after we called it with two floating point numbers. If we call the function with two integers, a version (called a *method*) that is optimized for integers is compiled. A version optimized for an integer and a rational number or any combination of arguments can also be compiled." ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 25,\\frac{37}{4} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 25,\\frac{37}{4} \\right] $$\"" ] }, "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[f(3,4), f(3, 1/2)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define two versions of the same function to see the difference in performance between compiled functions and functions defined via `Rule`s." ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [], "source": [ "(a = Range(0.0,100.,.01), ccossq = J(x -> cos(x)^2), cossq(x_) := cos(x)^2);" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [], "source": [ "Time(True);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We run each test twice because compilation time is included in the first run." ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 1.119352 seconds (2.99 M allocations: 182.794 MiB, 4.80% gc time)\n", "tryrule count: downvalue 10001, upvalue 0\n" ] } ], "source": [ "Map(cossq, a);" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.512223 seconds (1.93 M allocations: 125.300 MiB, 12.62% gc time)\n", "tryrule count: downvalue 10001, upvalue 0\n" ] } ], "source": [ "Map(cossq, a);" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.052542 seconds (189.87 k allocations: 4.259 MiB)\n", "tryrule count: downvalue 0, upvalue 0\n" ] } ], "source": [ "Map(ccossq, a);" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.006194 seconds (175.49 k allocations: 3.522 MiB)\n", "tryrule count: downvalue 0, upvalue 0\n" ] } ], "source": [ "Map(ccossq, a);" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.000184 seconds (115 allocations: 54.461 KiB)\n", "tryrule count: downvalue 0, upvalue 0\n" ] } ], "source": [ "(Time(False), ClearAll(f,a,ccossq,cossq,mylog))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The compiled function is about $500$ times faster in this example. Symata `Pattern`s can be compiled (automatically) as well, but this has been removed during a rewriting of the `Pattern` code that is still underway. With compiled `Pattern`s, the factor might be closer to $50$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Compiling Symata expressions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this example we calculate an expression and compile it. The compiled code is as efficient hand-coded Julia." ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\frac{- \\ \\mathbb{e} ^{x} \\ \\text{Cos} \\! \\left( x \\right) }{2} + \\frac{ \\mathbb{e} ^{x} \\ x^{2} \\ \\text{Cos} \\! \\left( x \\right) }{2} + \\frac{ \\mathbb{e} ^{x} \\ \\text{Sin} \\! \\left( x \\right) }{2} + \\frac{ \\mathbb{e} ^{x} \\ x^{2} \\ \\text{Sin} \\! \\left( x \\right) }{2} \\ - \\ \\mathbb{e} ^{x} \\ x \\ \\text{Sin} \\! \\left( x \\right) $$" ], "text/plain": [ "L\"$$ \\frac{- \\ \\mathbb{e} ^{x} \\ \\text{Cos} \\! \\left( x \\right) }{2} + \\frac{ \\mathbb{e} ^{x} \\ x^{2} \\ \\text{Cos} \\! \\left( x \\right) }{2} + \\frac{ \\mathbb{e} ^{x} \\ \\text{Sin} \\! \\left( x \\right) }{2} + \\frac{ \\mathbb{e} ^{x} \\ x^{2} \\ \\text{Sin} \\! \\left( x \\right) }{2} \\ - \\ \\mathbb{e} ^{x} \\ x \\ \\text{Sin} \\! \\left( x \\right) $$\"" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr = Integrate( x^2 * Exp(x)* Cos(x), x)" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\mathbb{e} ^{x} \\ \\left( \\frac{- \\ \\text{Cos} \\! \\left( x \\right) }{2} + \\frac{x^{2} \\ \\text{Cos} \\! \\left( x \\right) }{2} + \\frac{\\text{Sin} \\! \\left( x \\right) }{2} + \\frac{x^{2} \\ \\text{Sin} \\! \\left( x \\right) }{2} \\ - \\ x \\ \\text{Sin} \\! \\left( x \\right) \\right) $$" ], "text/plain": [ "L\"$$ \\mathbb{e} ^{x} \\ \\left( \\frac{- \\ \\text{Cos} \\! \\left( x \\right) }{2} + \\frac{x^{2} \\ \\text{Cos} \\! \\left( x \\right) }{2} + \\frac{\\text{Sin} \\! \\left( x \\right) }{2} + \\frac{x^{2} \\ \\text{Sin} \\! \\left( x \\right) }{2} \\ - \\ x \\ \\text{Sin} \\! \\left( x \\right) \\right) $$\"" ] }, "execution_count": 127, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr = Collect(expr, Exp(x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we use `Compile`. By default, `Compile` does not evauate its arguments. So we ask explicitly for evaluation." ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ #1 $$" ], "text/plain": [ "L\"$$ #1 $$\"" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cexpr = Compile([x], Evaluate(expr))" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0 \\right] $$\"" ] }, "execution_count": 129, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = Range(0.0, 10.0, 1.0)" ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ -0.5,0.0,-1.252973632244913,-73.86918111161395,-453.59806643015446,-633.3454347927651,5369.752738571736,32810.620246210805,58593.78763053668,-188456.81767159692,-1.40015405338426e6 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ -0.5,0.0,-1.252973632244913,-73.86918111161395,-453.59806643015446,-633.3454347927651,5369.752738571736,32810.620246210805,58593.78763053668,-188456.81767159692,-1.40015405338426e6 \\right] $$\"" ] }, "execution_count": 130, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Map(cexpr, a)" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [], "source": [ "a = Range(0.0,10.0,.01);" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 0.000569939,\\text{Null} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 0.000569939,\\text{Null} \\right] $$\"" ] }, "execution_count": 132, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Timing((Map(cexpr, a), Null))" ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [], "source": [ "# ClearAll(a,expr,cexpr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Version and date" ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symata version 0.4.6\n", "Julia version 1.6.0-DEV.116\n", "Python version 3.8.3\n", "SymPy version 1.5.1\n" ] } ], "source": [ "VersionInfo()" ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2020-05-31T08:19:32.746" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "InputForm(Now())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.6.0-DEV", "language": "julia", "name": "julia-1.6" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.6.0" } }, "nbformat": 4, "nbformat_minor": 1 }