Lecture 38: Introduction to Julia

Julia is a programming language which aims to combine the high productivity of scripting languages with the high performance of compiled languages. The paper [BEKS17] describes the language.

_images/figjulialang.png

Fig. 93 The logo of Julia, from the Software Engineering Daily web site.

Getting Started

In the cloud, at CoCalc: https://cocalc.com you can open a Jupyter notebook with a Julia kernel.

To install Julia on your own computer, follow the instructions at <https://julialang.org>. To execute Julia programs in a Jupyter notebook, do using IJulia followed by notebook() in the REPL of Julia, that is: when running in a Terminal or PowerShell window. With using one can install Plots, SymPy and Symbolics. The SymPy.jl is a wrapper to the Python package SymPy, whereas Symbolics [GMCGSER22] is a computer algebra system written in Julia.

To demonstrate symbolic computation with Julia, consider the problem of computing the slope of the tangent line at a point on a circle.

Implicit Differentiation with SymPy.jl

In the equation of the circle \(x^2 + y^2 - 1 = 0\), the variable \(x\) is the independent variable and the variable \(y\) depends on \(x\). We will obtain the slope via implicit differentiation, as \(\displaystyle \frac{d}{d~\!x} y(x)\).

using SymPy

If SymPy is not yet installed, then you will be prompted to install.

The declarations of the variables result in dy to be assigned the unknown slope \(\displaystyle \frac{d}{d~\!x} y(x)\).

x = Sym("x")
y = SymFunction("y")
dy = diff(y(x), x)

Observe that y is declared as a symbolic function, in any symbol. If we want to use y when we define the equation, then we must use y(x) to indicate that the function y depends on x.

c = x^2 + y(x)^2 - 1
dc = diff(c, x)
dydx = solve(dc, dy)

The solve returns an Array which starts in Julia with index one. The slope is then obtained as

symbolicslope = dydx[1]

and equals -x/y(x).

Let us now take a random point on the circle, generated by a random angle, using the polar representation.

angle = 2*pi*rand()
(a, b) = (cos(angle), sin(angle))
sd = Dict(x => a, y(x) => b)

The coordinates of the point (a, b) are stored in a dictionary, which is a convenient data structure for substitution. Observe that, as Julia is geared towards numerical computing, the pi is a 64-bit floating-point approximation for \(\pi\). The numerical value for the slope is then obtained via substitution as follows:

slope = symbolicslope.subs(sd)

The numerical value in slope serves as a reference to check the alternative computation of the slope via the Taylor series.

The series of SymPy works for one variable only, but it works well on purely symbolic expressions, so we can compute the expression for the tangent line by running series twice. Because we continue the session, we declare new symbols X and Y for the variables in the circle equation and A and B for the coordinates of the point.

X, Y, A, B = Sym("X, Y, A, B")
nc = X^2 + Y^2 - 1
cx = SymPy.series(nc, X, A, 2)

In cx is the Taylor series expansion of the expression stored in nc about X = A, of order 2. To convert the cx into a symbolic expression on which we can apply series again, we select the coefficient to make the expression px (a polynomial as truncated series).

px = cx.coeff(X, 0) + cx.coeff(X - A, 1)*(X - A)
cy = series(px, Y, B, 2)
cY = cy.subs(Dict(A => a, B => b)

Then the slope is obtained as

- cY.coeff(X, 1)/cY.coeff(Y, 1)

which agrees with the value earlier assigned to slope.

Implicit Differentiation with Symbolics.jl

We will now compute the value of the slope a third time.

using Symbolics.jl
@variables x, y(x)

Observe that with Symbolics (opposite to what we did with SymPy), we declare y(x) so y is explicitly defined as depending on y. This implies that typing y suffices and, unlike with SymPy, we do not need to type y(x) when we define the equation of the circle.

In order not to confuse with the Differential of SymPy, which we used earlier in this notebook, we declare that we use the Differential of Symbolics.

D = Symbolics.Differential(x)
dy = D(y)

As before, dy is \(\displaystyle \frac{d}{d~\!x} y(x)\).

c = x^2 + y^2 - 1
dc = D(c)

The differential operator D does not expand automatically, we have to ask for it.

eq = expand_derivatives(dc)
SBslope = Symbolics.solve_for(eq,dx)

In SBslope is the expression -x/y(x) so the symbolic part of this problem is thus indeed solved. What is left is the substitution, again using a dictionary, mapping the coordinates (a, b) of the random point to the symbols x and y.

nsd = Dict(x => a, y => b)
substitute(SBslope, nsd)

and we see again the value that was earlier assigned to slope.

References

[BEKS17]

J. Bezanson, A. Edelman, S. Karpinski, V.B. Shah. A fresh approach to numerical computing. SIAM Review 59(1): 65-98, 2017.

[GMCGSER22]

S. Gowda, Y. Ma, A. Cheli, M. Gwozdz, V.B. Shah, A. Edelman, and C. Rackauckas. High-performance symbolic-numerics via multiple dispatch, arxiv:2105.03949v3 [cs.CL] 5 Feb 2022.