Source code for sbmlmath.cfunction
"""Handling of ``<csymbol>`` functions"""
from __future__ import annotations
from sympy import Number
from sympy.core.function import UndefinedFunction
__all__ = ["CFunction", "delay", "rate_of", "Delay", "RateOf"]
DEF_URL_BASE = "http://www.sbml.org/sbml/symbols/"
DEF_URL_RATE_OF = DEF_URL_BASE + "rateOf"
DEF_URL_DELAY = DEF_URL_BASE + "delay"
[docs]
class CFunction(UndefinedFunction):
"""
Represents ``<csymbol>`` functions.
Examples: :func:`rateOf() <rate_of>`, :func:`delay`,
distributions from the `distrib package
<https://synonym.caltech.edu/documents/specifications/level-3/version-1/distrib/>`_.
See also https://www.w3.org/TR/MathML2/chapter4.html#contm.deffun.
"""
DEFINITION_URL = None
_cache = {}
_definition_url_to_derived_class = {}
def __new__(
cls: type[CFunction],
*args,
definition_url: str = None,
encoding: str = "text",
**kwargs,
):
definition_url = definition_url or cls.DEFINITION_URL
if definition_url is None:
raise ValueError("definition_url must be provided")
# Cache instances.
# If not done: (CFunction("A", definition_url="x")
# == CFunction("A", definition_url="y")) == False
if not (name := kwargs.get("name")):
if not len(args):
raise ValueError("name argument must be provided")
name = args[0]
cache_key = (name, definition_url, encoding)
if cached := cls._cache.get(cache_key):
return cached
obj = super().__new__(cls, *args, **kwargs)
# ensure there are no collisions with super() attributes
assert not hasattr(obj, "definition_url")
assert not hasattr(obj, "encoding")
obj.definition_url = definition_url
obj.encoding = encoding
if hasattr(cls, "eval"):
obj.eval = cls.eval
cls._cache[cache_key] = obj
return obj
def __eq__(self, other):
if not isinstance(other, CFunction):
return False
# if they represent the same value, they are equal
return self.definition_url == other.definition_url
def __hash__(self):
return hash(
(
self.__class__.__name__,
self.DEFINITION_URL,
self.definition_url,
self.name,
)
)
@classmethod
def register_subclass(cls, derived_class: type[CFunction]):
cls._definition_url_to_derived_class[derived_class.DEFINITION_URL] = (
derived_class
)
# Derived classes for specific SBML functions
[docs]
class Delay(CFunction):
"""Produces a SBML ``delay()`` function.
Usually, it's preferable to use the :func:`delay` function.
This class can be used if a *delay* function with a different name is
needed.
Examples:
>>> from sympy.abc import a
>>> my_delay = Delay("my_delay")
>>> my_delay(a)
my_delay(a)
>>> delay(a) == my_delay(a)
True
"""
DEFINITION_URL = DEF_URL_DELAY
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)
CFunction.register_subclass(Delay)
[docs]
class RateOf(CFunction):
"""Produces a SBML ``rateOf()`` function.
Usually, it's preferable to use the :func:`rate_of` function.
This class can be used if a *rateOf* function with a different name is
needed.
Examples:
>>> from sympy.abc import a
>>> my_rate_of = RateOf("my_rate_of")
>>> my_rate_of(a)
my_rate_of(a)
>>> rate_of(a) == my_rate_of(a)
True
>>> rate_of(1)
0
"""
DEFINITION_URL = DEF_URL_RATE_OF
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)
@classmethod
def eval(cls, x):
if isinstance(x, Number):
# the rate of a constant is 0
return 0
CFunction.register_subclass(RateOf)
# SBML-defined functions
#: The SBML ``delay()`` function.
delay = Delay("delay")
#: The SBML ``rateOf()`` function.
rate_of = RateOf("rateOf")