# ----------------------------------------------------------------------------# SymForce - Copyright 2022, Skydio, Inc.# This source code is under the Apache 2.0 license found in the LICENSE file.# ----------------------------------------------------------------------------importsymforce.internal.symbolicassffromsymforceimportopsfromsymforceimporttypingasT
[docs]classStorage:""" Interface for objects that implement the storage concept. Because this class is registered using :class:`symforce.ops.impl.class_storage_ops.ClassStorageOps` (see bottom of this file), any object that inherits from ``Storage`` and that implements the functions defined in this class can be used with the StorageOps concept. E.g. calling:: ops.StorageOps.storage_dim(my_obj) will return the same result as ``my_obj.storage_dim()`` if ``my_obj`` inherits from this class. """# Type that represents this or any subclassesStorageT=T.TypeVar("StorageT",bound="Storage")
[docs]@classmethoddefstorage_dim(cls)->int:""" Dimension of underlying storage """raiseNotImplementedError()
[docs]def__repr__(self:StorageT)->str:""" String representation of this type. """raiseNotImplementedError()
[docs]defto_storage(self:StorageT)->T.List[T.Scalar]:""" Flat list representation of the underlying storage, length of :meth:`storage_dim`. This is used purely for plumbing, it is NOT like a tangent space. """raiseNotImplementedError()
[docs]@classmethoddeffrom_storage(cls:T.Type[StorageT],elements:T.Sequence[T.Scalar])->StorageT:""" Construct from a flat list representation. Opposite of :meth:`to_storage`. """raiseNotImplementedError()
[docs]def__eq__(self:StorageT,other:T.Any)->bool:""" Returns exact equality between self and other. """ifnotisinstance(self,other.__class__):returnFalseself_list,other_list=self.to_storage(),other.to_storage()ifnotlen(self_list)==len(other_list):returnFalseifnotall(s==ofors,oinzip(self_list,other_list)):returnFalsereturnTrue
[docs]defsubs(self:StorageT,*args:T.Any,**kwargs:T.Any)->StorageT:""" Substitute given values of each scalar element into a new instance. """returnops.StorageOps.subs(self,*args,**kwargs)
# TODO(hayk): Way to get sf.simplify to work on these types directly?
[docs]defsimplify(self:StorageT)->StorageT:""" Simplify each scalar element into a new instance. """returnself.from_storage(sf.simplify(sf.sympy.Matrix(self.to_storage())))
[docs]@classmethoddefsymbolic(cls:T.Type[StorageT],name:str,**kwargs:T.Any)->StorageT:""" Construct a symbolic element with the given name prefix. Kwargs are forwarded to :class:`sf.Symbol <symforce.symbolic.Symbol>` (for example, sympy assumptions). """returncls.from_storage([sf.Symbol(f"{name}_{i}",**kwargs)foriinrange(cls.storage_dim())])
[docs]def__hash__(self)->int:""" Hash this object in immutable form, by combining all their scalar hashes. NOTE(hayk, nathan): This is somewhat dangerous because we don't always guarantee that Storage objects are immutable (e.g. sf.Matrix). If you add this object as a key to a dict, modify it, and access the dict, it will show up as another key because it breaks the abstraction that an object will maintain the same hash over its lifetime. """returntuple(self.to_storage()).__hash__()