Source code for symforce.ops

# ----------------------------------------------------------------------------
# SymForce - Copyright 2022, Skydio, Inc.
# This source code is under the Apache 2.0 license found in the LICENSE file.
# ----------------------------------------------------------------------------

"""
API for mathematical groups in python with minimal dependencies. Assumes elements
have appropriate methods, or for the case of scalar types (ints, floats, sympy.Symbols)
assumes that the group is reals under addition.

This is the recommended API for using these concepts, rather than calling directly on a type.
"""

import abc

import numpy as np

from .group_ops import GroupOps
from .lie_group_ops import LieGroupOps
from .storage_ops import StorageOps

# isort: split

import symforce.internal.symbolic as sf
from symforce import typing as T
from symforce import typing_util

from .impl.array_lie_group_ops import ArrayLieGroupOps
from .impl.databuffer_storage_ops import DataBufferStorageOps
from .impl.dataclass_lie_group_ops import DataclassLieGroupOps
from .impl.nonetype_lie_group_ops import NoneTypeLieGroupOps

# Register ops for scalars and sequences
from .impl.scalar_lie_group_ops import ScalarLieGroupOps
from .impl.sequence_lie_group_ops import SequenceLieGroupOps


[docs]class ScalarExpr(abc.ABC): """ Metaclass for scalar expressions :class:`symforce.symbolic.DataBuffer` is a subclass of :class:`sf.Expr <symforce.symbolic.Expr>` but we do not want it to be registered under :class:`ScalarLieGroupOps <symforce.ops.impl.scalar_lie_group_ops.ScalarLieGroupOps>`. """ @abc.abstractmethod def __init__(self, *args: T.Any, **kwargs: T.Any) -> None: pass @classmethod def __subclasshook__(cls, subclass: T.Type) -> bool: if issubclass(subclass, sf.DataBuffer): return False return issubclass(subclass, sf.Expr) and isinstance(subclass, type)
for scalar_type in typing_util.SCALAR_TYPES: LieGroupOps.register(scalar_type, ScalarLieGroupOps) LieGroupOps.register(ScalarExpr, ScalarLieGroupOps) LieGroupOps.register(list, SequenceLieGroupOps) LieGroupOps.register(tuple, SequenceLieGroupOps) LieGroupOps.register(np.ndarray, ArrayLieGroupOps) LieGroupOps.register(T.Dataclass, DataclassLieGroupOps) # We register NoneType to allow dataclasses to have optional fields which default to "None". LieGroupOps.register(type(None), NoneTypeLieGroupOps)
[docs]class LieGroupSymClass(abc.ABC): # noqa: B024 """ Metaclass for generated numeric geo classes We use a metaclass here to avoid having `symforce.ops` depend on `sym`, which would make it impossible to generate `sym` from scratch. TODO(aaron): SymClassLieGroupOps does the wrong thing for some methods, e.g. storage_D_tangent returns the wrong type. We should also implement this for the cam classes, which aren't lie groups. """ @staticmethod def __subclasshook__(subclass: T.Type) -> bool: for parent in subclass.__mro__: if parent.__module__.startswith("sym."): from symforce import geo from symforce.ops.interfaces.lie_group import LieGroup maybe_geo_class = getattr(geo, parent.__name__, None) if maybe_geo_class is not None and issubclass(maybe_geo_class, LieGroup): return True return False
from .impl.sym_class_lie_group_ops import SymClassLieGroupOps LieGroupOps.register(LieGroupSymClass, SymClassLieGroupOps) StorageOps.register(sf.DataBuffer, DataBufferStorageOps)