In this blog post, we will explore the Grassmann.jl library written in Julia for working with Clifford algebras.

First, we import the library and the GLMakie plotting library.

using Grassmann
using GLMakie
set_theme!(theme_light())

The following code constructs a positive definite 3-dimensional TensorBundle with an additional plane at infinity:

@basis S"∞+++"
(⟨∞111⟩, v, v∞, v₁, v₂, v₃, v∞₁, v∞₂, v∞₃, v₁₂, v₁₃, v₂₃, v∞₁₂, v∞₁₃, v∞₂₃, v₁₂₃, v∞₁₂₃)

then plot the following vector field:

$$ \exp\left(\frac{\frac{\pi}{2}(v_{12}+v_{\infty3})}{2}\right) $$

First we project it into the submanifold $\langle\infty++\_\rangle$:

streamplot(
  vectorfield(
    exp((π/2)*(v12+v∞3)/2),
    V(1,2,3),V(1,2,3)
  ),-1.5..1.5,-1.5..1.5,-1.5..1.5,gridsize=(12,12))

Then we project it into the submanifold $\langle\_+++\rangle$:

speed = Observable{Float64}(0.0)
vf = @lift(vectorfield(
  exp((π/2)*(v12+$speed*v∞3)/2),
  V(2,3,4),V(2,3,4)
))

fig, ax, pl = streamplot(vf,-1.5..1.5,-1.5..1.5,-1.5..1.5,gridsize=(12,12))

framerate = 24
timestamps = range(0, 2, step=1/framerate)

record(fig, "streamplot.mp4", timestamps; framerate = framerate) do t
    speed[] = Float64(t/10.0)
    # ax.azimuth[] = 1.7pi + 0.3 * sin(2pi * t / 120)
    # pl.colorrange = (0, t)
end
"streamplot.mp4"

basis"2" # Euclidean
streamplot(vectorfield(v1*exp((π/4)*v12/2)),-1.5..1.5,-1.5..1.5)
@basis S"+-" # Hyperbolic
streamplot(vectorfield(v1*exp((π/4)*v12/2)),-1.5..1.5,-1.5..1.5)
@basis S"∞+++"
f(t) = ((exp(π*t*((3/7)*v12+v∞3))>>>↑(v1+v2+v3)))
lines(V(2,3,4).(points(f)))
Base.@kwdef mutable struct Lorenz
    dt::Float64 = 0.01
    σ::Float64 = 10
    ρ::Float64 = 28
    β::Float64 = 8/3
    x::Float64 = 1
    y::Float64 = 1
    z::Float64 = 1
end

function step!(l::Lorenz)
    dx = l.σ * (l.y - l.x)
    dy = l.x * (l.ρ - l.z) - l.y
    dz = l.x * l.y - l.β * l.z
    l.x += l.dt * dx
    l.y += l.dt * dy
    l.z += l.dt * dz
    Point3f(l.x, l.y, l.z)
end

attractor = Lorenz()

lorenz_points = Observable(Point3f[])
colors = Observable(Int[])

set_theme!(theme_black())

fig, ax, l = lines(lorenz_points, color = colors,
    colormap = :inferno, transparency = true,
    axis = (; type = Axis3, protrusions = (0, 0, 0, 0),
              viewmode = :fit, limits = (-30, 30, -30, 30, 0, 50)))

record(fig, "lorenz.mp4", 1:120) do frame
    for i in 1:50
        push!(lorenz_points[], step!(attractor))
        push!(colors[], frame)
    end
    ax.azimuth[] = 1.7pi + 0.3 * sin(2pi * frame / 120)
    notify(lorenz_points)
    notify(colors)
    l.colorrange = (0, frame)
end
"lorenz.mp4"