diff --git a/clifford/__init__.py b/clifford/__init__.py index 85867aa4..ffcba8b9 100644 --- a/clifford/__init__.py +++ b/clifford/__init__.py @@ -11,7 +11,7 @@ Constructing algebras ===================== -Note that typically the :doc:`predefined-algebras` are sufficient, and there is no need to build an algebra from scratch. +Note that typically the :doc:`/predefined-algebras` are sufficient, and there is no need to build an algebra from scratch. .. autosummary:: :toctree: @@ -77,7 +77,7 @@ import os import itertools import warnings -from typing import List, Tuple, Set +from typing import List, Tuple, Set, Dict # Major library imports. import numpy as np @@ -318,11 +318,26 @@ def elements(dims: int, firstIdx=0) -> List[Tuple[int, ...]]: return list(_powerset(range(firstIdx, firstIdx + dims))) -def Cl(p=0, q=0, r=0, sig=None, names=None, firstIdx=1, mvClass=MultiVector): +def Cl(p: int = 0, q: int = 0, r: int = 0, sig=None, names=None, firstIdx=1, + mvClass=MultiVector) -> Tuple[Layout, Dict[str, MultiVector]]: r"""Returns a :class:`Layout` and basis blade :class:`MultiVector`\ s for the geometric algebra :math:`Cl_{p,q,r}`. The notation :math:`Cl_{p,q,r}` means that the algebra is :math:`p+q+r`-dimensional, with the first :math:`p` vectors with positive signature, the next :math:`q` vectors negative, and the final :math:`r` vectors with null signature. + Parameters + ---------- + p : int + number of positive-signature basis vectors + q : int + number of negative-signature basis vectors + r : int + number of zero-signature basis vectors + sig + See the docs for :class:`clifford.Layout`. If ``sig`` is passed, then + `p`, `q`, and `r` are ignored. + names, firstIdx + See the docs for :class:`clifford.Layout`. + Returns ======= layout : Layout @@ -346,13 +361,33 @@ def basis_vectors(layout): def randomMV( - layout, min=-2.0, max=2.0, grades=None, mvClass=MultiVector, - uniform=None, n=1, normed=False, rng=None): + layout: Layout, min=-2.0, max=2.0, grades=None, mvClass=MultiVector, + uniform=None, n=1, normed: bool = False, rng=None): """n Random MultiVectors with given layout. Coefficients are between min and max, and if grades is a list of integers, only those grades will be non-zero. + Parameters + ------------ + layout : Layout + the layout + min, max : Number + range of values from which to uniformly sample coefficients + grades : int, List[int] + grades which should have non-zero coefficients. If ``None``, defaults to + all grades. A single integer is treated as a list of one integers. + uniform : Callable[[Number, Number, Tuple[int, ...]], np.ndarray] + A function like `np.random.uniform`. Defaults to ``rng.uniform``. + n : int + The number of samples to generate. If ``n > 1``, this function + returns a list instead of a single multivector + normed : bool + If true, call :meth:`MultiVector.normal` on each multivector. Note + that this does not result in a uniform sampling of directions. + rng : + The random number state to use. Typical use would be to construct a + generator with :func:`numpy.random.default_rng`. Examples -------- @@ -387,7 +422,7 @@ def randomMV( return mv -def conformalize(layout, added_sig=[1, -1], *, mvClass=MultiVector, **kwargs): +def conformalize(layout: Layout, added_sig=[1, -1], *, mvClass=MultiVector, **kwargs): ''' Conformalize a Geometric Algebra @@ -406,7 +441,7 @@ def conformalize(layout, added_sig=[1, -1], *, mvClass=MultiVector, **kwargs): added_sig: list-like list of +1, -1 denoted the added signatures **kwargs : - passed to Cl() used to generate conformal layout + extra arguments to pass on into the :class:`Layout` constructor. Returns --------- diff --git a/clifford/_layout.py b/clifford/_layout.py index c72d6256..ebb5a43a 100644 --- a/clifford/_layout.py +++ b/clifford/_layout.py @@ -574,9 +574,7 @@ def comp_func(Xval): @_cached_property def _shirokov_inverse(self): - """ - Performs the inversion operation as described in Theorem 4, page 16 of the paper :cite:`shirokov2020inverse` - """ + """ See `MultiVector.shirokov_inverse` for documentation """ n = len(self.sig) exponent = (n + 1) // 2 N = 2 ** exponent @@ -594,9 +592,7 @@ def shirokov_inverse(U): @_cached_property def _hitzer_inverse(self): - """ - Performs the inversion operation as described in the paper :cite:`Hitzer_Sangwine_2017` - """ + """ See `MultiVector.hitzer_inverse` for documentation """ tot = len(self.sig) @_numba_utils.njit def hitzer_inverse(operand): diff --git a/clifford/_multivector.py b/clifford/_multivector.py index 8dea759d..e8ecb1ac 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -463,6 +463,10 @@ def __call__(self, other, *others) -> 'MultiVector': ``M(N)`` calls :meth:`project` as ``N.project(M)``. + .. versionchanged:: 1.4.0 + Grades larger than the dimension of the multivector now return 0 + instead of erroring. + Examples -------- >>> from clifford.g2 import * @@ -738,9 +742,25 @@ def normal(self) -> 'MultiVector': return self / abs(self) def hitzer_inverse(self): + """ + Obtain the inverse :math:`M^{-1}` via the algorithm in the paper + :cite:`Hitzer_Sangwine_2017`. + + .. versionadded:: 1.4.0 + + Raises + ------ + NotImplementedError : + on algebras with more than 5 non-null dimensions + """ return self.layout._hitzer_inverse(self) def shirokov_inverse(self): + """Obtain the inverse :math:`M^{-1}` via the algorithm in Theorem 4, + page 16 of Dmitry Shirokov's ICCA 2020 paper :cite:`shirokov2020inverse`. + + .. versionadded:: 1.4.0 + """ return self.layout._shirokov_inverse(self) def leftLaInv(self) -> 'MultiVector': @@ -792,6 +812,23 @@ def normalInv(self, check=True) -> 'MultiVector': return self._pick_inv(fallback=False if check else None) def inv(self) -> 'MultiVector': + r"""Obtain the inverse :math:`M^{-1}`. + + This tries a handful of approaches in order: + + * If :math:`M \tilde M = |M|^2`, then this uses + :meth:`~MultiVector.normalInv`. + * If :math:`M` is of sufficiently low dimension, this uses + :meth:`~MultiVector.hitzer_inverse`. + * Otherwise, this uses :meth:`~MultiVector.leftLaInv`. + + Note that :meth:`~MultiVector.shirokov_inverse` is not used as its + numeric stability is unknown. + + .. versionchanged:: 1.4.0 + Now additionally tries :meth:`~MultiVector.hitzer_inverse` before + falling back to :meth:`~MultiVector.leftLaInv`. + """ return self._pick_inv(fallback=True) leftInv = leftLaInv diff --git a/clifford/numba/__init__.py b/clifford/numba/__init__.py index 9c9e7679..ae993358 100644 --- a/clifford/numba/__init__.py +++ b/clifford/numba/__init__.py @@ -1,2 +1,111 @@ +r""" +.. currentmodule:: clifford.numba + +=============================================== +numba extension support (:mod:`clifford.numba`) +=============================================== + +.. versionadded:: 1.4.0 + +This module provides :mod:`numba` extension types :class:`MultiVectorType` and +:class:`LayoutType` corresponding to :class:`~clifford.MultiVector` and +:class:`~clifford.Layout`. + +You do not need to import this module to take advantage of these types; they +are needed only directly when writing numba overloads via +:func:`numba.extending.overload` and similar. + +As a simple example, the following code defines a vectorized ``up()`` function +for :doc:`CGA ` :: + + from clifford.g3c import * + + @numba.njit + def jit_up(x): + return eo + x + 0.5*abs(x)**2*einf + + assert up(e1) == jit_up(e1) + +Note that a rough equivalent to this particular function is provided elsewhere +as :func:`clifford.tools.g3c.fast_up`. + +.. currentmodule:: clifford + +Supported operations +-------------------- +The following list of operations are supported in a jitted context: + +* A limited version of the constructor ``MultiVector(layout, value)``, and + the alias :meth:`layout.MultiVector`. +* :attr:`MultiVector.value` +* :attr:`MultiVector.layout` +* Arithmetic: + + * :meth:`MultiVector.__add__` + * :meth:`MultiVector.__sub__` + * :meth:`MultiVector.__mul__` + * :meth:`MultiVector.__xor__` + * :meth:`MultiVector.__or__` + * :meth:`MultiVector.__pow__` + * :meth:`MultiVector.__truediv__` + * :meth:`MultiVector.__invert__` + * :meth:`MultiVector.__pos__` + * :meth:`MultiVector.__neg__` + +* :meth:`MultiVector.__call__` +* :meth:`MultiVector.mag2` +* :meth:`MultiVector.__abs__` +* :meth:`MultiVector.normal` +* :meth:`MultiVector.gradeInvol` +* :meth:`MultiVector.conjugate` + +Performance considerations +-------------------------- +While the resulted jitted code is typically faster, there are two main +performance issues to consider. The first is the startup time of ``@jit``\ ing. +This can be quite substantial, although can be somewhat alleviated by using +the ``cache=True`` argument to :func:`numba.jit`. +The second is the time taken for numba to find the appropriate dispatch loop +given the Python types of its arguments, which adds overhead to every call. + +``clifford`` tries as hard as possible to reduce this second overhead, by +using the undocumented ``_numba_type_`` attribute and keeping our own optimized +cache instead of going through the recommended +``@numba.extending.typeof_impl.register(LayoutType)`` mechanism. +However, this overhead can still be slow compared to elementary operations. +The following code is significantly impacted by this overhead:: + + from clifford.g3c import * + import numba + + @numba.njit + def mul(a, b): + return a * b + + # 286 ms, ignoring jitting time + x = e1 + for i in range(10000): + x = mul(x, x + e2) + +as each iteration of the loop pays it again. The overhead can be avoided by +jitting the entire loop:: + + from clifford.g3c import * + import numba + + @numba.njit + def mul(a, b): + return a * b + + @numba.njit + def the_loop(x): + for i in range(1000): + x = mul(x, x + e1) + return x + + # 2.4 ms, ignoring jitting time + x = the_loop(eq) + +""" from ._multivector import MultiVectorType from ._layout import LayoutType diff --git a/clifford/tools/__init__.py b/clifford/tools/__init__.py index 374c1609..c25ee0e4 100644 --- a/clifford/tools/__init__.py +++ b/clifford/tools/__init__.py @@ -31,7 +31,7 @@ ----------------------------------------------------------- Given two frames that are related by a orthogonal transform, we seek a rotor -which enacts the transform. Details of the mathematics and pseudo-code used the +which enacts the transform. Details of the mathematics and pseudo-code used to create the algorithms below can be found at Allan Cortzen's website, :cite:`ctz-frames`. @@ -443,9 +443,9 @@ def rotor_decomp(V: MultiVector, x: MultiVector) -> Tuple[MultiVector, MultiVect Returns ------- - H : clifford.Multivector + H : clifford.MultiVector rotor which contains x - U : clifford.Multivector + U : clifford.MultiVector rotor which leaves x invariant ''' diff --git a/clifford/tools/g3c/__init__.py b/clifford/tools/g3c/__init__.py index 5dd8f829..ab572144 100644 --- a/clifford/tools/g3c/__init__.py +++ b/clifford/tools/g3c/__init__.py @@ -7,6 +7,11 @@ Generation Methods -------------------- +.. versionchanged:: 1.4.0 + + Many of these functions accept an ``rng`` arguments, which if provided + should be the result of calling :func:`numpy.random.default_rng` or similar. + .. autosummary:: :toctree: generated/ diff --git a/docs/api/index.rst b/docs/api/index.rst index ccb38db8..d6509af4 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -11,3 +11,4 @@ API operator transformations taylor_expansions + numba diff --git a/docs/api/numba.rst b/docs/api/numba.rst new file mode 100644 index 00000000..b205fa8c --- /dev/null +++ b/docs/api/numba.rst @@ -0,0 +1 @@ +.. automodule:: clifford.numba diff --git a/docs/changelog.rst b/docs/changelog.rst index 20f76ea2..1870f7a2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,107 +6,133 @@ Changelog Changes in 1.4.x ++++++++++++++++ - * Projection using :meth:`Multivector.__call__` no longer raises :exc:`ValueError` - for grades not present in the algebra, and instead just returns zero. +* :class:`MultiVector` is now supported directly within ``@numba.njit``\ ed code. + See the documentation for this feature in the :mod:`clifford.numba` module for more details. + +* New algorithms for the multivector inverse :meth:`MultiVector.inv`: + + * Inverting a non-blade multivector in algebras where :math:`p = q \le 5` now falls back on the + approach described in :cite:`Hitzer_Sangwine_2017` instead of using a linear algebra approach. + This algorithm can be used directly via :meth:`MultiVector.hitzer_inverse`. + * An additional method is available, :meth:`MultiVector.shirokov_inverse`, + which is the arbitrary signature algorithm described in :cite:`shirokov2020inverse`. +* A new :mod:`clifford.taylor_expansions` module for Taylor series of various + multivector functions, starting with common trigonometric functions. These functions are + additionally exposed via methods on :class:`MultiVector` like :meth:`MultiVector.cos`. +* Random functions now accept an ``rng`` keyword argument that accepts the object returned + by :func:`numpy.random.default_rng`, for deterministic randomness. +* Projection using :meth:`MultiVector.__call__` as ``mv(grade)`` no longer raises :exc:`ValueError` + for grades not present in the algebra, and instead just returns zero. +* Some JIT-ed code is now cached when :mod:`clifford` is imported for the very first time, + speeding up subsequent imports. +* Improved citations in the documentation. - * Where possible, ``MultiVector``\ s preserve their data type in the dual, and - the right and left complements. - - * A new multivector inverse method is available, :meth:`clifford.MultiVector.shirokov_inverse`, - which is the arbitrary signature algorithm described - in Theorem 4, page 16 of Dmitry Shirokov's ICCA 2020 paper :cite:`shirokov2020inverse`. - - * A new :mod:`clifford.taylor_expansions` is added to implement taylor series of various - multivector functions, starting with common trigonometric functions. These functions are - additionally exposed via methods on the MultiVector class itself. +Bugs fixed +---------- +* Where possible, ``MultiVector``\ s preserve their data type in the dual, and + the right and left complements. +* :class:`MVArray` no longer errantly promotes 0-dimensional arrays to 1-dimensional arrays. +* :class:`MultiVector`\ s with :class:`complex` coefficients are now printed correctly. +* :meth:`cga.Round.radius` is no longer always 1. - * Random functions now accept an ``rng`` keyword argument that accepts the object returned - by :func:`numpy.random.default_rng`, for deterministic randomness. +Compatibility notes +------------------- +* :func:`clifford.general_exp` is deprecated in favor of :meth:`clifford.taylor_expansions.exp`, + although typically :meth:`clifford.MultiVector.exp` is a better choice +* Transparently treating a :class:`MultiVector` as a flat array of coefficients is deprecated, + and so ``mv[i]`` and ``len(mv)`` both emit :exc:`DeprecationWarning`s. If the underlying + storage order is of interest, use ``mv.value[i]`` and ``len(mv)``respectively. To obtain the + scalar part of a :class:`MultiVector`, use ``mv[()]`` instead of ``mv[0]``. +* ``Layout.gradeList`` has been removed. If still needed, it can be recovered as an :type:`ndarray` + isntead of a :class:`list` via the private attribute ``layout._basis_blade_order.grades`` + (:attr:`BasisBladeOrder.grades`). +* This will be the last release to support Python 3.5, which reached its end-of-life during our + last release cycle. Changes in 1.3.x ++++++++++++++++ - * Python 3.8 is officially supported. 1.2.0 was pinned to a bad numba version - that was incompatible with 3.8. - * A new :mod:`clifford.operator` module to contain the previously undocumented - :func:`~clifford.operator.gp`, :func:`~clifford.operator.op`, and - :func:`~clifford.operator.ip` helpers. - * A new :mod:`clifford.transformations` module for linear transformations. - * Two new :doc:`predefined-algebras`, :mod:`clifford.dpga` and :mod:`clifford.dg3c`. - * Improvements throughout the documentation: - - * Better overall structure, visible in the docs sidebar. - * New tutorials for :doc:`tutorials/cga/index` on visualization and applications. - * New tutorial on :doc:`tutorials/apollonius-cga-augmented`. - * New tutorial on :doc:`tutorials/linear-transformations`. - * New |launch binder| links at the top of each notebook tutorial, to run - examples from the browser. - * Faster algebra construction. ``Cl(3)`` is now 100\ |times| faster, and - ``Cl(6)`` is 20\ |times| faster. This is achieved by deferring product JIT - compilation until the product is used for the first time. - * Additional testing and assorted improvements for :mod:`clifford.tools.g3c`: - - * :func:`~clifford.tools.g3c.closest_point_on_circle_from_line` has now been implemented - roughly following the procedure described in Appendix A of - `Andreas Aristidou's PhD thesis `_. - * :func:`~clifford.tools.g3c.closest_point_on_line_from_circle` has now also been added, - projecting the result of :func:`~clifford.tools.g3c.closest_point_on_circle_from_line` - to the line. - * :func:`clifford.ugly` now results in less ugly output for - :doc:`predefined-algebras`. +* Python 3.8 is officially supported. 1.2.0 was pinned to a bad numba version + that was incompatible with 3.8. +* A new :mod:`clifford.operator` module to contain the previously undocumented + :func:`~clifford.operator.gp`, :func:`~clifford.operator.op`, and + :func:`~clifford.operator.ip` helpers. +* A new :mod:`clifford.transformations` module for linear transformations. +* Two new :doc:`predefined-algebras`, :mod:`clifford.dpga` and :mod:`clifford.dg3c`. +* Improvements throughout the documentation: + + * Better overall structure, visible in the docs sidebar. + * New tutorials for :doc:`tutorials/cga/index` on visualization and applications. + * New tutorial on :doc:`tutorials/apollonius-cga-augmented`. + * New tutorial on :doc:`tutorials/linear-transformations`. + * New |launch binder| links at the top of each notebook tutorial, to run + examples from the browser. +* Faster algebra construction. ``Cl(3)`` is now 100\ |times| faster, and + ``Cl(6)`` is 20\ |times| faster. This is achieved by deferring product JIT + compilation until the product is used for the first time. +* Additional testing and assorted improvements for :mod:`clifford.tools.g3c`: + + * :func:`~clifford.tools.g3c.closest_point_on_circle_from_line` has now been implemented + roughly following the procedure described in Appendix A of + `Andreas Aristidou's PhD thesis `_. + * :func:`~clifford.tools.g3c.closest_point_on_line_from_circle` has now also been added, + projecting the result of :func:`~clifford.tools.g3c.closest_point_on_circle_from_line` + to the line. +* :func:`clifford.ugly` now results in less ugly output for + :doc:`predefined-algebras`. .. |launch binder| image:: https://mybinder.org/badge_logo.svg Bugs fixed ---------- - * :meth:`MultiVector.meet` no longer produces zero erroneously. - * ``mv[e1 + e12]`` now raises :exc:`ValueError`, rather than being interpreted - as ``mv[e1]``. - * :func:`~clifford.operator.ip` (the inner product) no longer performs the - outer product. - * :func:`Layout.parse_multivector` now throws :exc:`SyntaxError` on invalid - input, rather than silenltly producing nonsense. - * :func:`Layout.parse_multivector` supports basis vector names which do not - start with e. - * In :mod:`clifford.tools.g3c`: - - * :func:`~clifford.tools.g3c.val_midpoint_between_lines` now handles the case that - the two lines are touching. - * :func:`~clifford.tools.g3c.object_fitting.val_fit_circle` now correctly selects the first and - second eigenvalue regardless of order. - * :func:`~clifford.tools.g3c.sphere_beyond_plane` now tested and correct. - * :func:`~clifford.tools.g3c.sphere_behind_plane` now tested and correct. - * :func:`~clifford.tools.g3c.val_unsign_sphere` is now jitted, as it should have - been from the start. - * :func:`~clifford.tools.g3c.get_nearest_plane_point` correctly returns the conformal - point rather than the 3D point. +* :meth:`MultiVector.meet` no longer produces zero erroneously. +* ``mv[e1 + e12]`` now raises :exc:`ValueError`, rather than being interpreted + as ``mv[e1]``. +* :func:`~clifford.operator.ip` (the inner product) no longer performs the + outer product. +* :func:`Layout.parse_multivector` now throws :exc:`SyntaxError` on invalid + input, rather than silenltly producing nonsense. +* :func:`Layout.parse_multivector` supports basis vector names which do not + start with e. +* In :mod:`clifford.tools.g3c`: + + * :func:`~clifford.tools.g3c.val_midpoint_between_lines` now handles the case that + the two lines are touching. + * :func:`~clifford.tools.g3c.object_fitting.val_fit_circle` now correctly selects the first and + second eigenvalue regardless of order. + * :func:`~clifford.tools.g3c.sphere_beyond_plane` now tested and correct. + * :func:`~clifford.tools.g3c.sphere_behind_plane` now tested and correct. + * :func:`~clifford.tools.g3c.val_unsign_sphere` is now jitted, as it should have + been from the start. + * :func:`~clifford.tools.g3c.get_nearest_plane_point` correctly returns the conformal + point rather than the 3D point. Compatibility notes ------------------- - * ``clifford.grades_present`` is deprecated in favor of - :meth:`MultiVector.grades`, the latter of which now takes an ``eps`` argument. - * ``del mv[i]`` is no longer legal, the equivalent ``mv[i] = 0`` should be used instead. - * ``Layout.dict_to_multivector`` has been removed. It was accidentally broken - in 1.0.5, so there is little point deprecating it. - * :meth:`Layout.basis_names` now returns a ``list`` of ``str``, rather than a - numpy array of ``bytes``. The result now matches the construction order, rather - than being sorted alphabetically. The order of :meth:`Layout.metric` has - been adjusted for consistency. - * The ``imt_prod_mask``, ``omt_prod_mask``, and ``lcmt_prod_mask`` attributes - of :class:`Layout` objects have been removed, as these were an unnecessary - intermediate computation that had no need to be public. - * Some functions in :mod:`clifford.tools.g3c` have been renamed: - - * ``closest_points_on_circles`` has been renamed to - :func:`~clifford.tools.g3c.iterative_closest_points_on_circles`. - * ``closest_points_circle_line`` has been renamed to - :func:`~clifford.tools.g3c.iterative_closest_points_circle_line`. - * ``furthest_points_on_circles`` has been renamed to - :func:`~clifford.tools.g3c.iterative_furthest_points_on_circles`. - * While this release is compatible with :mod:`numba` version 0.49.0, it is - recommended to use 0.48.0 which does not emit as many warnings. See the - :doc:`installation` instructions for how to follow this guidance. +* ``clifford.grades_present`` is deprecated in favor of + :meth:`MultiVector.grades`, the latter of which now takes an ``eps`` argument. +* ``del mv[i]`` is no longer legal, the equivalent ``mv[i] = 0`` should be used instead. +* ``Layout.dict_to_multivector`` has been removed. It was accidentally broken + in 1.0.5, so there is little point deprecating it. +* :meth:`Layout.basis_names` now returns a ``list`` of ``str``, rather than a + numpy array of ``bytes``. The result now matches the construction order, rather + than being sorted alphabetically. The order of :meth:`Layout.metric` has + been adjusted for consistency. +* The ``imt_prod_mask``, ``omt_prod_mask``, and ``lcmt_prod_mask`` attributes + of :class:`Layout` objects have been removed, as these were an unnecessary + intermediate computation that had no need to be public. +* Some functions in :mod:`clifford.tools.g3c` have been renamed: + + * ``closest_points_on_circles`` has been renamed to + :func:`~clifford.tools.g3c.iterative_closest_points_on_circles`. + * ``closest_points_circle_line`` has been renamed to + :func:`~clifford.tools.g3c.iterative_closest_points_circle_line`. + * ``furthest_points_on_circles`` has been renamed to + :func:`~clifford.tools.g3c.iterative_furthest_points_on_circles`. +* While this release is compatible with :mod:`numba` version 0.49.0, it is + recommended to use 0.48.0 which does not emit as many warnings. See the + :doc:`installation` instructions for how to follow this guidance. Patch releases -------------- @@ -115,102 +141,102 @@ Patch releases Changes in 1.2.x ++++++++++++++++ - * ``layout.isconformal``, ``layout.einf``, and ``layout.eo``, which were added - in 1.0.4, have been removed. The first can now be spelt - ``isinstance(layout, clifford.ConformalLayout)``, and the other properties - now exist only on :class:`ConformalLayout` objects. - * :meth:`MultiVector.left_complement` has been added for consistency with - :meth:`MultiVector.right_complement`. - * A new :mod:`clifford.tools.classify` module has been added for classifying - blades. - * :class:`Layout` objects print slightly more cleanly in Jupyter notebooks. - * :attr:`Layout.scalar` is now integral rather than floating point +* ``layout.isconformal``, ``layout.einf``, and ``layout.eo``, which were added + in 1.0.4, have been removed. The first can now be spelt + ``isinstance(layout, clifford.ConformalLayout)``, and the other properties + now exist only on :class:`ConformalLayout` objects. +* :meth:`MultiVector.left_complement` has been added for consistency with + :meth:`MultiVector.right_complement`. +* A new :mod:`clifford.tools.classify` module has been added for classifying + blades. +* :class:`Layout` objects print slightly more cleanly in Jupyter notebooks. +* :attr:`Layout.scalar` is now integral rather than floating point Bugs fixed ---------- - * ``pow(mv, 0)`` gives the right result - * ``nan`` is now printed correctly when it appears in multivectors. Previously it was hidden - * :meth:`MultiVector.right_complement` no longer performs the left complement. - * :meth:`MultiVector.vee` has been corrected to have the same sign as +* ``pow(mv, 0)`` gives the right result +* ``nan`` is now printed correctly when it appears in multivectors. Previously it was hidden +* :meth:`MultiVector.right_complement` no longer performs the left complement. +* :meth:`MultiVector.vee` has been corrected to have the same sign as :meth:`MultiVector.meet` Compatibility notes ------------------- - * :attr:`Layout.scalar` is now integral rather than floating point, to match - :attr:`Layout.pseudoScalar`. +* :attr:`Layout.scalar` is now integral rather than floating point, to match + :attr:`Layout.pseudoScalar`. Changes in 1.1.x ++++++++++++++++ - * Restores ``layout.gmt``, ``Layout.omt``, ``Layout.imt``, and ``Layout.lcmt``. - A few releases ago, these existed but were dense. - For memory reasons, they were then removed entirely. - They have now been reinstated as :class:`sparse.COO` matrix objects, which - behave much the same as the original dense arrays. +* Restores ``layout.gmt``, ``Layout.omt``, ``Layout.imt``, and ``Layout.lcmt``. + A few releases ago, these existed but were dense. + For memory reasons, they were then removed entirely. + They have now been reinstated as :class:`sparse.COO` matrix objects, which + behave much the same as the original dense arrays. - * ``MultiVector``\ s preserve their data type in addition, subtraction, and - products. This means that integers remain integers until combined with - floats. Note that this means in principle integer overflow is possible, so - working with floats is still recommended. This also adds support for floating - point types of other precision, such as ``np.float32``. +* ``MultiVector``\ s preserve their data type in addition, subtraction, and + products. This means that integers remain integers until combined with + floats. Note that this means in principle integer overflow is possible, so + working with floats is still recommended. This also adds support for floating + point types of other precision, such as ``np.float32``. - * ``setup.py`` is now configured such that ``pip2 install clifford`` will not - attempt to download this version, since it does not work at all on python 2. +* ``setup.py`` is now configured such that ``pip2 install clifford`` will not + attempt to download this version, since it does not work at all on python 2. - * Documentation now includes examples of ``pyganja`` visualizations. +* Documentation now includes examples of ``pyganja`` visualizations. Compatibility notes ------------------- - * ``Layout.blades()`` now includes the scalar ``1``, as do other similar - functions. +* ``Layout.blades()`` now includes the scalar ``1``, as do other similar + functions. - * ``MultiVector.grades()`` now returns a :class:`set` not a :class:`list`. - This means code like ``mv.grades() == [0]`` will need to change to - ``mv.grades() == {0}``, or to work both before and after this change, - ``set(mv.grades()) == {0}``. +* ``MultiVector.grades()`` now returns a :class:`set` not a :class:`list`. + This means code like ``mv.grades() == [0]`` will need to change to + ``mv.grades() == {0}``, or to work both before and after this change, + ``set(mv.grades()) == {0}``. Bugs fixed ---------- - * ``mv[(i, j)]`` would sometimes fail if the indices were not in canonical order. - * ``mv == None`` and ``layout == None`` would crash rather than return ``False``. - * ``blade.isVersor()`` would return ``False``. - * ``layout.blades_of_grade(0)`` would not return the list it claimed to return. +* ``mv[(i, j)]`` would sometimes fail if the indices were not in canonical order. +* ``mv == None`` and ``layout == None`` would crash rather than return ``False``. +* ``blade.isVersor()`` would return ``False``. +* ``layout.blades_of_grade(0)`` would not return the list it claimed to return. Internal changes ---------------- - * Switch to ``pytest`` for testing. - * Enable code coverage. - * Split into smaller files. - * Remove python 2 compatibility code, which already no longer worked. +* Switch to ``pytest`` for testing. +* Enable code coverage. +* Split into smaller files. +* Remove python 2 compatibility code, which already no longer worked. Changes 0.6-0.7 +++++++++++++++++ - * Added a real license. - * Convert to NumPy instead of Numeric. +* Added a real license. +* Convert to NumPy instead of Numeric. Changes 0.5-0.6 +++++++++++++++++ - * ``join()`` and ``meet()`` actually work now, but have numerical accuracy problems - * added ``clean()`` to :class:`MultiVector` - * added ``leftInv()`` and ``rightInv()`` to :class:`MultiVector` - * moved ``pseudoScalar()`` and ``invPS()`` to :class:`MultiVector` (so we can derive - new classes from :class:`MultiVector`) - * changed all of the instances of creating a new MultiVector to create - an instance of ``self.__class__`` for proper inheritance - * fixed bug in laInv() - * fixed the massive confusion about how dot() works - * added left-contraction - * fixed embarrassing bug in gmt generation - * added ``normal()`` and ``anticommutator()`` methods - * fixed dumb bug in :func:`elements()` that limited it to 4 dimensions +* ``join()`` and ``meet()`` actually work now, but have numerical accuracy problems +* added ``clean()`` to :class:`MultiVector` +* added ``leftInv()`` and ``rightInv()`` to :class:`MultiVector` +* moved ``pseudoScalar()`` and ``invPS()`` to :class:`MultiVector` (so we can derive + new classes from :class:`MultiVector`) +* changed all of the instances of creating a new MultiVector to create + an instance of ``self.__class__`` for proper inheritance +* fixed bug in laInv() +* fixed the massive confusion about how dot() works +* added left-contraction +* fixed embarrassing bug in gmt generation +* added ``normal()`` and ``anticommutator()`` methods +* fixed dumb bug in :func:`elements()` that limited it to 4 dimensions Acknowledgements +++++++++++++++++ diff --git a/docs/requirements.txt b/docs/requirements.txt index a53bf63f..e7fff654 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -5,7 +5,7 @@ ipykernel nbsphinx ipywidgets sphinx>=2.4 -sphinx_rtd_theme +sphinx_rtd_theme>=0.5.0 sphinxcontrib-bibtex<2.0.0 # needed to avoid https://github.com/sphinx-doc/sphinx/issues/8198