elixir-nx / nx

Multi-dimensional arrays (tensors) and numerical definitions for Elixir
2.66k stars 194 forks source link

Geometric / Clifford algebra in arbitrary dimensions #1466

Closed nileshtrivedi closed 8 months ago

nileshtrivedi commented 8 months ago

I'm new to both Elixir and NX so, although it might be easy, I might still need a few pointers.

I was wondering if it is possible to do geometric algebra of vectors using NX.

For example in 3D, Geometric Algebra product of two vectors [x,y,z] and [p,q,r] would produce a multivector with a scalar term and a bivector term.

Ideally, the library would allow me to do geometric product in any number of dimensions of any kind. For example, The R(3,0,1) algebra would involve 3 positive, 0 negative and 1 zero dimension (i.e. the unit vectors square to +1, -1 and 0 respectively).

Here is an example of doing the same with numpy and clifford libraries in Python.

Here's an example in Javascript using ganja.js which lets me use an arbitrary metric signature like Algebra(3,0,1) and cleverly leverages the syntax for scientific notation for numbers to interpret them as k-vectors. So, 3.2e12 is interpreted not as 3.2 x 10^12 but 3.12 * the unit bivector e12.

Are things like this possible to achieve in NX?

polvalente commented 8 months ago

This is not currently possible in Nx, nor is planned. For what it's worth, the example you showed only uses numpy for getting constants like e and pi, while the actual algebra is handled by the clifford library.

nileshtrivedi commented 8 months ago

@polvalente True. Are you aware of any elixir libraries that I can use in conjunction with Nx to do this kind of work?

polvalente commented 8 months ago

@nileshtrivedi I'm not aware of any libraries in that space (heh) But you could build upon Nx for that. I read up a bit on Clifford Algebra and this is roughly where you would start for R2:

defmodule Geometric do
  defmodule Bivector do
    @moduledoc "represents a bivector with basis (e1, e2)"
    @derive {Nx.Container, containers: [:scalar]}
    defstruct [:scalar]
  end

  defmodule Multivector do
    @moduledoc "represents a multivector with a grade-0 (scalar) part and a grade-2 (bivector) part"
    @derive {Nx.Container, containers: [:scalar, :bivector]}
    defstruct [:scalar, :bivector]
  end

  import Nx.Defn

  defn wedge_product(%Nx.Tensor{shape: {n}} = v1, %Nx.Tensor{shape: {n}} = v2) do
    a = v1[0]
    b = v1[1]
    c = v2[0]
    d = v2[1]

    %Bivector{scalar: a * d - b * c}
  end

  defn geometric_product(%Nx.Tensor{shape: {n}} = v1, %Nx.Tensor{shape: {n}} = v2) do
    %MultiVector{scalar: Nx.dot(v1, v2), bivector: wedge_product(v1, v2)}
  end
end

Nx.Container allows you to define structs that work inside defn, and then you can compose things accordingly, even defining a custom add function that works differently based on the different input combinations.

nileshtrivedi commented 8 months ago

@polvalente Oh, thank you so much for taking the time to look into this (much more than I was expecting). This helps a lot. :-)