Skip to content

algebra.util

Utilities for working with the algebra of sundials and analemma

project_bivector(bivec, target_frame, render_frame, simp=lambda mv: sp.trigsimp(sp.expand_trig(mv)))

Project a bivector onto a basis in one Geometric Algebra (GA), and express that basis in terms of another GA

Given a bivector bivec as \(B\) and the target_frame \(A_1, A_2, A_3\), form \(-B\cdot A_i\) for \(i = 1,2,3\) and return the bivector

\(\sum_i f(-B\cdot A_i) \, B_i\)

where the frame \(B_1, B_2, B_3\) is given by render_frame and \(f()\) is the simplification function in simp. For example, it is often useful to work in terms of the surface frame

\(n_1\wedge n_2,\, n_1\wedge n_3,\, n_2\wedge n_3\),

but sometimes we need vectors in that space expressed (rendered) in terms of the fixed stars frame

\(e_1\wedge e_2,\, e_1\wedge e_3,\, e_2\wedge e_3\).

Parameters:

Name Type Description Default
bivec Mv

Bivector to be projected

required
target_frame Tuple[Mv]

Bivector frame on which to evaluate the components of vec

required
render_frame Tuple[Mv]

Bivector frame to which the components will be multiplied

required
simp Callable[[Mv], Mv]

Simplification function \(f()\)

lambda mv: trigsimp(expand_trig(mv))

Returns:

Type Description
Mv

Projected, simplified and rendered bivector

Source code in src/analemma/algebra/util.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def project_bivector(
    bivec: mv.Mv,
    target_frame: Tuple[mv.Mv],
    render_frame: Tuple[mv.Mv],
    simp: Callable[[mv.Mv], mv.Mv] = lambda mv: sp.trigsimp(sp.expand_trig(mv)),
) -> mv.Mv:
    r"""
    Project a bivector onto a basis in one Geometric Algebra (GA), and express that basis in terms of another GA

    Given a bivector `bivec` as $B$ and the `target_frame` $A_1, A_2, A_3$, form $-B\cdot A_i$ for $i = 1,2,3$ and return the
    bivector

    $\sum_i f(-B\cdot A_i) \, B_i$

    where the frame $B_1, B_2, B_3$ is given by `render_frame` and $f()$ is the simplification function in `simp`. For
    example, it is often useful to work in terms of the [surface frame][analemma.algebra.frame.surface_bivec]

    $n_1\wedge n_2,\, n_1\wedge n_3,\, n_2\wedge n_3$,

    but sometimes we need vectors in that space expressed (rendered) in terms of the fixed stars frame

    $e_1\wedge e_2,\, e_1\wedge e_3,\, e_2\wedge e_3$.

    Parameters:
        bivec: Bivector to be projected
        target_frame: Bivector frame on which to evaluate the components of `vec`
        render_frame: Bivector frame to which the components will be multiplied
        simp: Simplification function $f()$

    Returns:
        Projected, simplified and rendered bivector
    """
    coeffs = [simp((-bivec | b).obj) for b in target_frame]
    return sum([c * b for c, b in zip(coeffs, render_frame)])

project_vector(vec, target_frame, render_frame, simp=lambda mv: sp.trigsimp(mv))

Project a vector onto a basis in one Geometric Algebra (GA), and express that basis in terms of another GA

Given a vector vec as \(v\) and the target_frame \(a_1, a_2, a_3\), form \(v\cdot a_i\) for \(i = 1,2,3\) and return the vector

\(\sum_i f(v\cdot a_i) \, b_i\)

where the frame \(b_1, b_2, b_3\) is given by render_frame and \(f()\) is the simplification function in simp. For example, it is often useful to work in terms of the surface frame \(n_1, n_2, n_3\), but sometimes we need vectors in that space expressed (rendered) in terms of the fixed stars frame \(e_1, e_2, e_3\).

Parameters:

Name Type Description Default
vec Mv

Vector to be projected

required
target_frame Tuple[Mv]

Frame on which to evaluate the components of vec

required
render_frame Tuple[Mv]

Frame to which the components will be multiplied

required
simp Callable[[Mv], Mv]

Simplification function \(f()\)

lambda mv: trigsimp(mv)

Returns:

Type Description
Mv

Projected, simplified and rendered vector

Source code in src/analemma/algebra/util.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def project_vector(
    vec: mv.Mv,
    target_frame: Tuple[mv.Mv],
    render_frame: Tuple[mv.Mv],
    simp: Callable[[mv.Mv], mv.Mv] = lambda mv: sp.trigsimp(mv),
) -> mv.Mv:
    r"""
    Project a vector onto a basis in one Geometric Algebra (GA), and express that basis in terms of another GA

    Given a vector `vec` as $v$ and the `target_frame` $a_1, a_2, a_3$, form $v\cdot a_i$ for $i = 1,2,3$ and return the
    vector

    $\sum_i f(v\cdot a_i) \, b_i$

    where the frame $b_1, b_2, b_3$ is given by `render_frame` and $f()$ is the simplification function in `simp`. For
    example, it is often useful to work in terms of the [surface frame][analemma.algebra.frame.surface] $n_1, n_2, n_3$,
    but sometimes we need vectors in that space expressed (rendered) in terms of the fixed stars frame $e_1, e_2, e_3$.

    Parameters:
        vec: Vector to be projected
        target_frame: Frame on which to evaluate the components of `vec`
        render_frame: Frame to which the components will be multiplied
        simp: Simplification function $f()$

    Returns:
        Projected, simplified and rendered vector
    """
    coeffs = [simp((vec | b).obj) for b in target_frame]
    return sum([c * b for c, b in zip(coeffs, render_frame)])

rotate(mv, angle, bivec)

Rotate a multivector

Forms the rotor \(R\) that rotates mv by angle in the plane bivec. Denote:

  • mv by \(M\)
  • angle by \(\theta\)
  • bivec by \(B\)

Then \(R = \exp(-B\theta/2) = \cos(\theta/2) - B\sin(\theta/2)\)

and this the function returns \(R\,M\,\tilde{R}\).

Used extensively in analemma.algebra.frame.

Parameters:

Name Type Description Default
mv Mv

Multivector to be rotated

required
angle float

Angle of rotation

required
bivec Mv

Unit bivector defining the plane of rotation

required

Returns:

Type Description
Mv

The multivector mv rotated by angle in plane bivector

Source code in src/analemma/algebra/util.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def rotate(mv: mv.Mv, angle: float, bivec: mv.Mv) -> mv.Mv:
    r"""
    Rotate a multivector

    Forms the rotor $R$ that rotates `mv` by `angle` in the plane `bivec`. Denote:

    * `mv` by $M$
    * `angle` by $\theta$
    * `bivec` by $B$

    Then $R = \exp(-B\theta/2) = \cos(\theta/2) - B\sin(\theta/2)$

    and this the function returns $R\,M\,\tilde{R}$.

    Used extensively in [analemma.algebra.frame][].

    Parameters:
        mv: Multivector to be rotated
        angle: Angle of rotation
        bivec: Unit bivector defining the plane of rotation

    Returns:
        The multivector `mv` rotated by `angle` in plane `bivector`
    """
    rotor = (cos(angle / 2) - (bivec) * sin(angle / 2)).trigsimp()
    return rotor * mv * rotor.rev()

update_coeffs(vec, simp=sp.trigsimp)

Apply an algebraic manipulation to the components of a vector

There is a behaviour, perhaps a bug, in galgebra.ga.Ga whereby the internal sympy objects holding the coefficients of a multiector remain in the state they were before the latest manipulation, and out of sync with what is observed when the multivector is rendered. This can result in subsequent simplifications failing to achieve the desired goal.

This function exists to address this behaviour by applying the manipulation (typically a simplification) in simp to the coefficients of vec individual and reforming the vector with the results.

Parameters:

Name Type Description Default
vec Mv

Vector to be updated

required
simp Callable[[Mv], Mv]

Algebraic manipulation to apply to the coefficients of vec

trigsimp

Returns:

Type Description
Mv

The result of applying simp to each component of vec individually then recombining with the basis vectors in vec

Source code in src/analemma/algebra/util.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def update_coeffs(vec: mv.Mv, simp: Callable[[mv.Mv], mv.Mv] = sp.trigsimp) -> mv.Mv:
    """
    Apply an algebraic manipulation to the components of a vector

    There is a behaviour, perhaps a bug, in [galgebra.ga.Ga](https://galgebra.readthedocs.io/en/latest/) whereby the
    internal sympy objects holding the coefficients of a multiector remain in the state they were before the latest
    manipulation, and out of sync with what is observed when the multivector is rendered. This can result in subsequent
    simplifications failing to achieve the desired goal.

    This function exists to address this behaviour by applying the manipulation (typically a simplification) in `simp`
    to the coefficients of `vec` individual and reforming the vector with the results.

    Parameters:
        vec: Vector to be updated
        simp: Algebraic manipulation to apply to the coefficients of `vec`

    Returns:
        The result of applying `simp` to each component of `vec` individually then recombining with the basis vectors in `vec`
    """
    return sum([simp(coeff) * vec for coeff, vec in zip(vec.get_coefs(1), vec.Ga.mv())])