Skip to content

Commit

Permalink
Deprecate the sequence-like methods of MultiVector (#346)
Browse files Browse the repository at this point in the history
Closes gh-344, closes gh-345.

In my opinion it is wrong to say that a multivector _is_ a sequence of coefficients - it happens to _have_ a sequence of coefficients, and if you want to access these you get all the sequence behavior through `.value`.

This also remove all but one use of these methods - the last one is internal to numpy, and will stop emitting warnings once we conclude the deprecation anyway.
  • Loading branch information
eric-wieser committed Jun 30, 2020
1 parent 3bcbbdb commit bb5ac25
Show file tree
Hide file tree
Showing 13 changed files with 93 additions and 71 deletions.
2 changes: 1 addition & 1 deletion clifford/_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ def metric(self) -> np.ndarray:
self._metric = np.zeros((len(basis_vectors), len(basis_vectors)))
for i, v in enumerate(basis_vectors):
for j, v2 in enumerate(basis_vectors):
self._metric[i, j] = (v | v2)[0]
self._metric[i, j] = (v | v2)[()]
return self._metric.copy()
else:
return self._metric.copy()
Expand Down
38 changes: 30 additions & 8 deletions clifford/_multivector.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import numbers
import math
from typing import List, Set, Tuple, Union
import warnings

import numpy as np

Expand Down Expand Up @@ -380,8 +381,14 @@ def __float__(self) -> float:
# sequence special methods
def __len__(self) -> int:
"""Returns length of value array.
"""
.. deprecated:: 1.4.0
Use ``self.layout.gaDims`` or ``len(self.value)`` instead.
"""
warnings.warn(
"Treating MultiVector objects like a sequence is deprecated. "
"To access the coefficients as a sequence, use the `.value` attribute.",
DeprecationWarning, stacklevel=2)
return self.layout.gaDims

def __getitem__(self, key: Union['MultiVector', tuple, int]) -> numbers.Number:
Expand All @@ -390,7 +397,10 @@ def __getitem__(self, key: Union['MultiVector', tuple, int]) -> numbers.Number:
If key is a blade tuple (e.g. ``(0, 1)`` or ``(1, 3)``), or a blade,
(e.g. ``e12``), then return the (real) value of that blade's coefficient.
Otherwise, treat key as an index into the list of coefficients.
.. deprecated:: 1.4.0
If an integer is passed, it is treated as an index into ``self.value``.
Use ``self.value[i]`` directly.
"""
if isinstance(key, MultiVector):
inds, = np.nonzero(key.value)
Expand All @@ -400,20 +410,32 @@ def __getitem__(self, key: Union['MultiVector', tuple, int]) -> numbers.Number:
elif isinstance(key, tuple):
sign, idx = self.layout._sign_and_index_from_tuple(key)
return sign*self.value[idx]
return self.value[key]
else:
warnings.warn(
"Treating MultiVector objects like a sequence is deprecated. "
"To access the coefficients as a sequence, use the `.value` attribute.",
DeprecationWarning, stacklevel=2)
return self.value[key]

def __setitem__(self, key: Union[tuple, int], value: numbers.Number) -> None:
"""
Implements ``self[key] = value``.
If key is a blade tuple (e.g. (0, 1) or (1, 3)), then set
the (real) value of that blade's coeficient.
Otherwise treat key as an index into the list of coefficients.
.. deprecated:: 1.4.0
If an integer is passed, it is treated as an index into ``self.value``.
Use ``self.value[i]`` directly.
"""
if isinstance(key, tuple):
sign, idx = self.layout._sign_and_index_from_tuple(key)
self.value[idx] = sign*value
else:
warnings.warn(
"Treating MultiVector objects like a sequence is deprecated. "
"To access the coefficients as a sequence, use the `.value` attribute.",
DeprecationWarning, stacklevel=2)
self.value[key] = value

# grade projection
Expand Down Expand Up @@ -637,7 +659,7 @@ def isVersor(self) -> bool:
"""
Vhat = self.gradeInvol()
Vrev = ~self
Vinv = Vrev/(self*Vrev)[0]
Vinv = Vrev/(self*Vrev)[()]

# Test if the versor inverse (~V)/(V * ~V) is truly the inverse of the
# multivector V
Expand Down Expand Up @@ -675,10 +697,10 @@ def blades_list(self) -> List['MultiVector']:
'''
ordered list of blades present in this MV
'''
blades_list = self.layout.blades_list
value = self.value
b = [v*b for v, b in zip(self.value, self.layout.blades_list)]

b = [value[0]] + [value[k]*blades_list[k] for k in range(1, len(self))]
# note that by doing Mv != 0 instead of coef != 0 we add float eps to
# our comparison
return [k for k in b if k != 0]

def normal(self) -> 'MultiVector':
Expand Down
6 changes: 3 additions & 3 deletions clifford/_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ def parse_multivector(layout: Layout, mv_string: str) -> MultiVector:
coeff = data

elif t == 'blade' and last_t == 'wedge':
mv_out[data] += coeff
mv_out.value[data] += coeff
elif t == 'blade' and last_t == 'sign':
mv_out[data] += sign
mv_out.value[data] += sign
elif t == 'blade' and last_t is None:
mv_out[data] += 1
mv_out.value[data] += 1

elif t == 'wedge' and last_t == 'coeff':
pass
Expand Down
2 changes: 1 addition & 1 deletion clifford/dg3c.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def down_cga1(point_cga1):
"""
Take a point in CGA
"""
return (point_cga1/-(point_cga1|einf1)[0]).value[1:4]
return (point_cga1/-(point_cga1|einf1)[()]).value[1:4]


def up_cga2(pnt_vector):
Expand Down
2 changes: 1 addition & 1 deletion clifford/dpga.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def up(threedDvec):


def down(pnt):
return np.array([(pnt|wis)[0] for wis in [w1s, w2s, w3s]])/((pnt|w0s)[0])
return np.array([(pnt|wis)[()] for wis in [w1s, w2s, w3s]])/((pnt|w0s)[()])


def dual_point(point):
Expand Down
10 changes: 5 additions & 5 deletions clifford/test/test_clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,11 +487,11 @@ def test_metric(self, g4_1):
e4 = layout.blades['e4']
e5 = layout.blades['e5']

assert (e1 * e1)[0] == 1
assert (e2 * e2)[0] == 1
assert (e3 * e3)[0] == 1
assert (e4 * e4)[0] == 1
assert (e5 * e5)[0] == -1
assert (e1 * e1)[()] == 1
assert (e2 * e2)[()] == 1
assert (e3 * e3)[()] == 1
assert (e4 * e4)[()] == 1
assert (e5 * e5)[()] == -1

def test_vee(self, g3c):
layout = g3c
Expand Down
2 changes: 1 addition & 1 deletion clifford/test/test_dpga.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def test_non_orthogonal_metric(self):

w_metric = np.array([
[
(a | b)[0]
(a | b)[()]
for a in wbasis
]
for b in wbasis
Expand Down
2 changes: 1 addition & 1 deletion clifford/test/test_g3c_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ def test_normalise_n_minus_1(self):
for i in range(500):
mv = np.random.rand() * random_conformal_point()
mv_normed = normalise_n_minus_1(mv)
npt.assert_almost_equal((mv_normed | ninf)[0], -1.0)
npt.assert_almost_equal((mv_normed | ninf)[()], -1.0)

def test_get_properties_of_sphere(self):
for i in range(100):
Expand Down
10 changes: 5 additions & 5 deletions clifford/tools/g3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def quaternion_to_rotor(quaternion):
Q = layout.MultiVector()
Q.value[1:4] = quaternion[1:4]
Q = -e123*Q
Q[0] = quaternion[0]
Q.value[0] = quaternion[0]
return Q


Expand All @@ -71,7 +71,7 @@ def rotor_to_quaternion(R):
Converts a pure rotation rotor into a quaternion
"""
Q = (e123*R).value[0:4]
Q[0] = R[0]
Q[0] = R.value[0]
return Q


Expand Down Expand Up @@ -167,7 +167,7 @@ def generate_rotation_rotor(theta, euc_vector_m, euc_vector_n):
euc_vector_n = euc_vector_n / abs(euc_vector_n)
euc_vector_m = euc_vector_m / abs(euc_vector_m)
bivector_B = (euc_vector_m ^ euc_vector_n)
bivector_B = bivector_B / (math.sqrt((-bivector_B * bivector_B)[0]))
bivector_B = bivector_B / (math.sqrt((-bivector_B * bivector_B)[()]))
rotor = math.cos(theta / 2) - bivector_B * math.sin(theta / 2)
return rotor

Expand All @@ -181,7 +181,7 @@ def angle_between_vectors(v1, v2):
"""
Returns the angle between two conformal vectors
"""
clipped = np.clip((v1 | v2)[0], -1.0, 1.0)
clipped = np.clip((v1 | v2)[()], -1.0, 1.0)
return math.acos(clipped)


Expand All @@ -196,7 +196,7 @@ def np_to_euc_mv(np_in):

def euc_mv_to_np(euc_point):
""" Converts a 3d GA point to a 3d numpy vector """
return euc_point[1:4].copy()
return euc_point.value[1:4].copy()


def euc_cross_prod(euc_a, euc_b):
Expand Down

0 comments on commit bb5ac25

Please sign in to comment.