Galixir.Algebras.PGA2 (galixir v0.24.0)

Copy Markdown View Source

This module implements two-dimensional Euclidean geometry using the projective geometric algebra (PGA) model.

Points, lines, and rigid-body transformations are encoded as multivectors, allowing geometric constructions to be expressed through algebraic operations such as the wedge product, meet, join, and sandwich product.

Cl(2,0,1)

with signature:

{1,1,0} # e1*e1 = 1, e2*e2 = 1, e0*e0 = 0

and basis:

e1, e2, e0

where e0 is the ideal (infinite) basis vector.

This module uses the dual representation of projective geometric algebra. In this representation, geometric objects are represented by their dual blades. In two-dimensional PGA this means:

  • Lines are represented as grade-1 vectors.
  • Points are represented as grade-2 bivectors.

This duality is a property of the representation, not a change in the underlying geometry. In this representation, the join operation is implemented through dualization and the wedge product, while the meet operation uses the wedge product directly.

join(a, b) = undual(wedge(dual(a), dual(b)))
meet(a, b) = wedge(a, b)

With this module's basis convention, finite points are represented as:

P = e12 + x*e20 + y*e01

where the coefficient of e12 is the homogeneous scale factor.

Ideal points have a zero e12 component:

P = x*e20 + y*e01

Examples

iex> p = Galixir.Algebras.PGA2.point(2, 3)
iex> Galixir.Algebras.PGA2.point_coordinates(p)
{2.0, 3.0}

Summary

Functions

Adds two multivectors component-wise.

Checks whether a multivector is a blade.

Returns the mapping between blade names and storage indices.

Returns the canonical sign of a multivector.

Returns the coefficient of a basis blade.

Returns the dimension of the algebra.

Returns the ideal point representing the direction of a line.

Extracts the Euclidean direction vector of a line.

Computes Euclidean distance between two finite points.

Computes the dual of a multivector.

Checks whether a PGA point is finite.

Computes the geometric product of two multivectors.

Returns the grade of a homogeneous multivector.

Extracts the grade-g component of a multivector.

Checks whether a multivector contains components of the given grade only.

Returns the grades present in a multivector.

Creates an ideal point representing a direction.

Checks whether a PGA point is ideal.

Tests whether two objects are incident.

Computes the inner product of two multivectors.

Computes the inverse of a multivector.

Checks whether a multivector contains components of the given grade only.

A guard that matches multi vectors that are all zero.

Computes the join of two geometric objects.

Computes the left contraction of two multivectors.

Creates the line through two points.

Creates a line from coefficients.

Creates a line from a normal vector and a point.

Returns the normal vector of a line.

Returns the maximum absolute coefficient of a multivector.

Computes the meet of two geometric objects.

Returns the norm of a multivector.

Normalizes a multivector.

Returns the normalized direction vector of a line.

Returns the normalized line normal vector.

Returns the scalar identity element.

Returns the Euclidean origin point.

Creates a finite projective point.

Extracts Cartesian coordinates from a finite point.

Applies the reverse operation to a multivector.

Computes the right contraction of two multivectors.

Creates a rotation motor around the origin.

Computes the rotor that maps one frame to another.

Checks whether a multivector contains only a scalar component.

Returns the scalar coefficient of a multivector.

Computes the scalar product of two multivectors.

Creates a multivector from a string representation.

Returns the metric signature of the algebra.

Returns the number of coefficients stored by the algebra.

Returns the squared norm of a multivector.

Subtracts two multivectors component-wise.

Returns the multiplication table for the algebra.

Formats a multivector using standard geometric algebra notation.

Applies a motor transformation.

Creates a translation motor from a vector.

Creates a translation motor.

Computes the inverse dual operation.

Creates a Euclidean direction vector.

Computes the outer product (wedge product) of two multivectors.

Computes the outer product of a list of multivectors.

Returns the zero multivector.

Checks whether all coefficients of a multivector are zero.

Functions

add(arg1, arg2)

Adds two multivectors component-wise.

Examples

iex> a = new(scalar: 2)
iex> b = new(scalar: 3)
iex> add(a, b)
new(scalar: 5)

basis_name(int)

blade?(a)

Checks whether a multivector is a blade.

A blade is a multivector containing components from at most one grade.

Scalars are considered blades.

Examples

iex> blade?(new(e1: 2))
true

iex> blade?(new(e12: 1))
true

iex> blade?(new(e1: 1, e2: 1))
true

iex> blade?(new(scalar: 2, e1: 2))
false

iex> blade?(new(e2: 2, e12: 2))
false

blade_indices()

Returns the mapping between blade names and storage indices.

Blade coefficients are stored in a fixed-size tuple. This map translates canonical blade names into their corresponding tuple index.

Example

iex> blade_indices()[:e1]
1

blade_inverse(b)

canonical_sign(pga2)

Returns the canonical sign of a multivector.

The canonical sign is determined by the first non-zero coefficient in storage order.

Returns:

  • 1 if the first non-zero coefficient is positive
  • -1 if the first non-zero coefficient is negative
  • 0 if all coefficients are zero

Examples

iex> canonical_sign(new(e1: 2))
1

iex> canonical_sign(new(e1: -2))
-1

iex> canonical_sign(new())
0

canonical_sign_tuple(arg)

canonicalize(a)

coefficient(pga2, blade)

Returns the coefficient of a basis blade.

The requested blade can be given in canonical form or as any registered blade alias. Aliases are automatically converted to the canonical blade and the appropriate sign is applied.

## Examples

iex> coefficient( ...> new(e1: 3), ...> :e1 ...> ) 3.0

commutator(a, b)

dimension()

Returns the dimension of the algebra.

This is the number of basis vectors defined by the signature.

direction_point(line)

Returns the ideal point representing the direction of a line.

The result is a point at infinity, not a Euclidean vector.

direction_vector(line)

Extracts the Euclidean direction vector of a line.

Examples

iex> direction_vector(line(point(0,1), point(2,0)))
new(e1: 2, e2: -1)

distance(a, b)

Computes Euclidean distance between two finite points.

iex> distance(point(1,2), point(1,2)) 0.0

iex> distance(point(-1,-1), point(2,3)) 5.0

dual(pga2)

Computes the dual of a multivector.

The dual maps each basis blade to its complementary blade with the appropriate orientation sign. The complement is determined by the full pseudoscalar of the algebra.

The operation is linear and applies independently to every coefficient.

Examples

iex> dual(new(e1: 1)) |> inspect
new(e20: 1.0) |> inspect

dual_tuple(arg)

finite_point?(p)

Checks whether a PGA point is finite.

A finite point has a non-zero homogeneous e12 component.

Examples

iex> finite_point?(point(1,2)) true

iex> finite_point?(ideal_point(1,2)) false

iex> finite_point?(vector(1,2)) false

gp(lhs, rhs)

Computes the geometric product of two multivectors.

The geometric product is the fundamental multiplication operation of geometric algebra. It combines the outer product and metric-dependent inner product into a single associative operation.

The result depends on the algebra's metric signature.

Examples

iex> gp(
...>   new(e1: 1),
...>   new(e1: 1)
...> )
new(scalar: 1)

grade(x)

Returns the grade of a homogeneous multivector.

The zero multivector is considered grade 0.

Returns nil for mixed-grade multivectors.

iex> grade( ...> new(scalar: 1) ...> ) 0

iex> grade( ...> new(e1: 2) ...> ) 1

iex> grade( ...> new(scalar: 1, e1: 2) ...> ) nil

iex> grade( ...> new() ...> ) nil

grade(t, g)

Extracts the grade-g component of a multivector.

All coefficients whose basis blades are not of grade g are set to zero.

Raises ArgumentError if g is outside the range 0..dimension().

Examples

iex> grade(
...>   new(scalar: 1, e1: 2),
...>   1
...> )
new(e1: 2)

iex> grade(
...>   new(scalar: 1, e1: 2),
...>   0
...> )
new(scalar: 1)

grade?(mv, g)

Checks whether a multivector contains components of the given grade only.

A multivector is considered to have a grade if all non-zero components belong to that grade. The zero multivector is considered to have grade 0.

Examples

iex> grade?(new(e1: 1), 1)
true

iex> grade?(new(scalar: 1, e1: 2), 2)
false

iex> grade?(new(scalar: 1), 0)
true

iex> grade?(new(), 0)
true

See is_grade/2

grades(arg1)

Returns the grades present in a multivector.

The returned list contains every grade with at least one non-zero coefficient, ordered from lowest to highest.

Examples

iex> grades(
...>   new(scalar: 1)
...> )
[0]

iex> grades(
...>   new(e1: 2)
...> )
[1]

iex> grades(
...>   new(scalar: 1, e1: 2)
...> )
[0, 1]

iex> grades(
...>   new()
...> )
[]

ideal_point(x, y)

Creates an ideal point representing a direction.

Ideal points are points at infinity and have no finite location.

Note that Euclidean vectors are not ideal points. Use direction_point/1 to convert a line direction into an ideal point.

ideal_point?(p)

Checks whether a PGA point is ideal.

An ideal point has zero homogeneous e12 component.

Examples

iex> ideal_point?(ideal_point(1,0))
true

iex> ideal_point?(point(1,0))
false

iex> ideal_point?(vector(1,0))
false

incident?(a, b)

Tests whether two objects are incident.

In PGA, two objects are incident when their join vanishes.

Note that identical objects are incident with themselves. Parallel distinct lines are not incident.

incident?/2 is not the same as geometric intersection. Parallel coincident lines are incident.

This includes cases such as:

  • a point lying on a line
  • an ideal point representing the direction of a line
  • identical geometric objects

Examples

iex> incident?(point(0,0), line(0,1,0))
true

iex> p = point(1, 2)
iex> l = line(point(1, 2), point(3, 4))
iex> incident?(p, l)
true

iex> p = point(1, 2)
iex> l = line(point(0, 0), point(1, 0))
iex> incident?(p, l)
false

iex> l1 = line(point(0, 0), point(1, 0))
iex> l2 = line(point(2, 0), point(3, 0))
iex> incident?(l1, l2)
true

iex> incident?(point(0, 0), line(point(0, 0), point(1, 0)))
true

iex> incident?(point(0, 1), line(point(0, 0), point(1, 0)))
false

iex> incident?(ideal_point(1, 0), line(point(0, 0), point(1, 0)))
true

iex> p = ideal_point(1, 0)
iex> l = line(point(0, 0), point(1, 0))
iex> incident?(p, l)
true

iex> p = ideal_point(0, 1)
iex> l = line(point(0, 0), point(1, 0))
iex> incident?(p, l)
false

inner(arg1, arg2)

Computes the inner product of two multivectors.

The operation is generated from the geometric product and retains only terms satisfying the grade selection rule.

Example

iex> inner(
...>   new(e1: 2),
...>   new(e1: 3)
...> )

inverse(a)

Computes the inverse of a multivector.

The inverse is computed using the reverse:

inverse(a) = reverse(a) / scalar_part(a * reverse(a))

This formula is valid when a * reverse(a) is a non-zero scalar.

Raises ArgumentError if the multivector is not invertible by this formula.

## Examples

    iex> inverse(
    ...>   new(e1: 2)
    ...> )|> inspect
    new(e1: 0.5) |> inspect

is_blade(mv)

(macro)

is_grade(mv, grade)

(macro)

Checks whether a multivector contains components of the given grade only.

A multivector is considered to have a grade if all non-zero components belong to that grade. The zero multivector is considered to have grade 0.

The is_grade(vector, grade) guard can be used in function guards:

def foo(mv) when is_grade(mv, 1) do
  mv
end

Examples

iex> grade?(new(e1: 1), 1)
true

iex> grade?(new(scalar: 1, e1: 2), 2)
false

iex> grade?(new(scalar: 1), 0)
true

iex> grade?(new(), 0)
true

See grade?/2

is_zero(mv)

(macro)

A guard that matches multi vectors that are all zero.

Can be used in function guards:

def foo(x) when is_zero(x), do: x

join(a, b)

Computes the join of two geometric objects.

The join returns the smallest blade containing both geometric objects.

It is implemented using the dual outer product:

a  b = dual(dual(a)  dual(b))

In PGA, common cases are:

point  point -> line
point  line -> top-grade element (pseudoscalar, ie. trivector)

In this dual representation, incidence is tested by the vanishing of the join.

Examples

iex> p1 = point(0, 0)
iex> p2 = point(1, 0)
iex> incident?(p1, line(p1, p2))
true

left_contraction(arg1, arg2)

Computes the left contraction of two multivectors.

The operation is generated from the geometric product and retains only terms satisfying the grade selection rule.

Example

iex> left_contraction(
...>   new(e1: 2),
...>   new(e1: 3)
...> )

line(a, b)

Creates the line through two points.

Equivalent to:

join(point1, point2)

Examples

iex> l = line(point(23, 42), point(3,4))
iex> incident?(point(23,42), l)
true

line(a, b, c)

Creates a line from coefficients.

Represents the equation:

ax + by + c = 0

as the PGA vector:

a*e1 + b*e2 + c*e0

The line is represented up to scale.

iex> normalize(line(1,2,3)) == normalize(line(2,4,6)) true

line_from_normal_point(n, p)

Creates a line from a normal vector and a point.

line_normal(l)

Returns the normal vector of a line.

The normal is:

a*e1 + b*e2

max_abs_component(pga2)

Returns the maximum absolute coefficient of a multivector.

Accepts either a multivector struct or the internal coefficient tuple.

Example

iex> max_abs_component(new(e1: 2, scalar: 5))
5.0

iex> max_abs_component(new(e1: 5, scalar: 2))
5.0

max_abs_component_tuple(arg)

meet(a, b)

Computes the meet of two geometric objects.

The meet returns the intersection of geometric objects. In this PGA representation it is computed using the outer product:

a  b

Common case:

line ∧ line -> point

For non-intersecting objects, the result may be an ideal object or zero, depending on the geometric configuration.

Examples

iex> l1 = line(point(0, 0), point(1, 0))
iex> l2 = line(point(0, 0), point(0, 1))
iex> p = meet(l1, l2)
iex> point_coordinates(p)
{0.0, 0.0}

new(basis \\ [])

norm(a)

Returns the norm of a multivector.

The norm is the square root of the absolute squared norm.

Example

iex> a = new(scalar: 3)
iex> norm(a)
3.0

normalize(a)

Normalizes a multivector.

The result has unit norm while preserving the direction of the multivector.

Raises ArgumentError when attempting to normalize a null multivector.

Example

iex> a = new(scalar: 2)
iex> norm(normalize(a))
1.0

normalized_direction_vector(line)

Returns the normalized direction vector of a line.

normalized_line_normal(l)

Returns the normalized line normal vector.

one()

Returns the scalar identity element.

origin()

Returns the Euclidean origin point.

point(x, y, w \\ 1)

Creates a finite projective point.

The point is represented homogeneously as:

P = e12 + x*e20 + y*e01

The optional w argument controls the homogeneous scale. Normally points should be created using the default value 1. The homogeneous scale w must not be zero.

Examples

iex> point_coordinates(point(1, 2))
{1.0, 2.0}

iex> point_coordinates(point(1, 2, 2))
{1.0, 2.0}

iex> point_coordinates(point(1, 2, 0.5))
{1.0, 2.0}

point_coordinates(p)

Extracts Cartesian coordinates from a finite point.

Returns:

{x, y}

Examples

iex> point_coordinates(origin()) {0.0, 0.0}

iex> point_coordinates(point(2, 3)) {2.0, 3.0}

reverse(pga2)

Applies the reverse operation to a multivector.

Reverse (also called reversion) changes the sign of basis blades according to their grade:

grade 0:  +
grade 1:  +
grade 2:  -
grade 3:  -
grade 4:  +
...

For a blade with grade r, the sign is:

(-1)^(r(r-1)/2)

Examples

iex> reverse(new(e1: 2))
new(e1: 2)

iex> reverse(new(e12: 2))
new(e12: -2)

iex> reverse(new(scalar: 3))
new(scalar: 3)

reverse_tuple(arg)

right_contraction(arg1, arg2)

Computes the right contraction of two multivectors.

The operation is generated from the geometric product and retains only terms satisfying the grade selection rule.

Example

iex> right_contraction(
...>   new(e1: 2),
...>   new(e1: 3)
...> )

rotor(angle)

Creates a rotation motor around the origin.

The angle is specified in radians.

Examples

iex> p = point(1,0)
iex> {x, y} = point_coordinates(transform(rotor(:math.pi / 2), p))
iex> {Float.round(x, 10), Float.round(y, 10)}
{0.0, 1.0}

iex> p = point(3,4)
iex> {x,y} = point_coordinates(transform(rotor(-:math.pi/2),
...>   transform(rotor(:math.pi/2), p)))
iex> {Float.round(x,10), Float.round(y,10)}
{3.0,4.0}

rotor_between_frames(source, target)

Computes the rotor that maps one frame to another.

The function constructs blades from the source and target frames and computes the transformation rotor:

R = normalize(1 + T * S⁻¹)

where S is the source frame blade and T is the target frame blade.

The resulting rotor can be applied to multivectors to rotate the source frame into the target frame.

scalar?(pga2)

Checks whether a multivector contains only a scalar component.

Components with an absolute value smaller than eps are considered zero.

Examples

iex> scalar?(new(scalar: 3))
true

iex> scalar?(new(e1: 3))
false

iex> scalar?(new())
true

scalar?(arg, eps \\ 1.0e-12)

scalar_part(pga2)

Returns the scalar coefficient of a multivector.

This is equivalent to retrieving the coefficient of the scalar blade.

Examples

iex> scalar_part(new(scalar: 5.0, e1: 2.0))
5.0

scalar_product(a, b)

Computes the scalar product of two multivectors.

The result depends on the metric of the algebra.

In PGA2, points are represented as bivectors:

P = e12 + x*e20 + y*e01

They are not null elements as conformal points are in conformal geometric algebra. The metric of PGA2 gives:

scalar_product(P, P) = -1

for normalized finite points.

Examples

iex> scalar_product(vector(1, 2), vector(3, 4))
11.0

iex> scalar_product(new(e1: 1), new(e1: 2))
2.0

iex> scalar_product(point(1, 2), point(1, 2))
-1.0

scale(s, s)

sigil_G(arg, list)

(macro)

Creates a multivector from a string representation.

The ~G sigil provides a convenient syntax for constructing multivectors using basis blades and coefficients.

Examples:

iex> ~G"e1 + e2"
new(e1: 1, e2: 1)

iex> ~G"2e1 - 0.5e12"
new(e1: 2, e12: -0.5)

iex> ~G"e12"
new(e12: 1)

iex> ~G"3"
new(scalar: 3)

The parsed expression is converted into the same representation accepted by new/1, so blade ordering and signs are handled by the algebra implementation.

signature()

Returns the metric signature of the algebra.

Example:

{1, 1, 1, 0}

represents a projective geometric algebra with three Euclidean basis vectors and one null basis vector.

size()

Returns the number of coefficients stored by the algebra.

A dimension n algebra contains 2^n basis blades.

squared_norm(a)

Returns the squared norm of a multivector.

The squared norm is computed as:

scalar_part(a * reverse(a))

The result may be negative for algebras with indefinite metrics.

Example

iex> a = new(scalar: 3)
iex> squared_norm(a)
9.0

sub(arg1, arg2)

Subtracts two multivectors component-wise.

Examples

iex> a = new(scalar: 5)
iex> b = new(scalar: 2)
iex> sub(a, b)
new(scalar: 3)

table()

Returns the multiplication table for the algebra.

The table contains precomputed geometric products between basis blades. Each entry maps {left_blade, right_blade} to {coefficient, result_blade}.

The blades are represented internally as bitmasks.

Example

iex> table() |> Map.has_key?({1, 1})
true

to_string(v)

Formats a multivector using standard geometric algebra notation.

Zero coefficients are omitted. Coefficients of 1 and -1 are elided for non-scalar basis blades.

Examples

iex> inspect(new())
"0"

iex> inspect(new(scalar: 2))
"2.0"

iex> inspect(new(e1: 1))
"e1"

iex> inspect(new(scalar: 1, e1: 2))
"1.0 + 2.0e1"

transform(motor, object)

Applies a motor transformation.

Uses the sandwich product:

M X M¹

translator(v)

Creates a translation motor from a vector.

Examples

iex> m = translator(vector(3, 4))
iex> point_coordinates(transform(m, origin()))
{3.0, 4.0}

translator(x, y)

Creates a translation motor.

Translates by the vector (x,y).

Examples

iex> m = translator(3, 4)
iex> point_coordinates(transform(m, origin()))
{3.0, 4.0}


iex> point_coordinates(transform(translator(3, 4), origin()))
{3.0, 4.0}

iex> point_coordinates(transform(translator(-2, 5), point(1, 2)))
{-1.0, 7.0}

iex> p = point(1, 2)
iex> point_coordinates(transform(translator(0, 0), p))
{1.0, 2.0}

iex> m = translator(3,4)
iex> point_coordinates(transform(inverse(m), transform(m, point(5,6))))
{5.0,6.0}

undual(pga2)

Computes the inverse dual operation.

undual/1 reverses the blade complement operation performed by dual/1.

For non-degenerate Euclidean algebras this corresponds to applying the dual operation twice with the appropriate pseudoscalar factor. In degenerate algebras the result depends on the implemented dual convention.

Examples

iex> undual(dual(new(e1: 2)))
new(e1: 2)

undual_tuple(arg)

vector(x, y)

Creates a Euclidean direction vector.

wedge(arg1, arg2)

Computes the outer product (wedge product) of two multivectors.

The wedge product combines blades by joining their basis vectors. It is antisymmetric:

a  b = -(b  a)

and vanishes when the operands share a basis vector.

Examples

iex> wedge(
...>   new(e1: 1),
...>   new(e2: 1)
...> )
new(e12: 1)

iex> wedge(
...>   new(e2: 1),
...>   new(e1: 1)
...> )
new(e12: -1)

iex> wedge(
...>   new(e1: 1),
...>   new(e1: 1)
...> )
new()

wedge_all(vectors)

Computes the outer product of a list of multivectors.

The vectors are combined from left to right using the wedge product.

The result is a blade representing the subspace spanned by all input multivectors.

zero()

Returns the zero multivector.

zero?(arg1)

Checks whether all coefficients of a multivector are zero.

Examples

iex> zero?(new())
true

iex> zero?(new(e1: 1))
false