aspartik.b3.parameters

class Scalable:

@runtime_checkable
class Scalable(Protocol):
    def scale(self, factor: float) -> int:
        """
        Scales all values of a parameter and returns the number of dimensions.
        """

        ...
#

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...

def scale(self, factor: float) -> int

    def scale(self, factor: float) -> int:
        """
        Scales all values of a parameter and returns the number of dimensions.
        """

        ...
#

Scales all values of a parameter and returns the number of dimensions.

class Real:

class Real(Stateful, SupportsFloat, Scalable):
    __slots__ = ("_value", "_backup")
    _value: float
    _backup: float

    def __init__(self, value: SupportsFloat):
        self._value = float(value)
        self._backup = self._value

    # comparison
    def __lt__(self, other: SupportsFloat) -> bool:
        return self._value < float(other)

    def __le__(self, other: SupportsFloat) -> bool:
        return self._value <= float(other)

    def __eq__(self, other: object) -> bool:
        if isinstance(other, SupportsFloat):
            return self._value == float(other)
        else:
            return False

    def __ne__(self, other: object) -> bool:
        if isinstance(other, SupportsFloat):
            return self._value != float(other)
        else:
            return False

    def __gt__(self, other: SupportsFloat) -> bool:
        return self._value > float(other)

    def __ge__(self, other: SupportsFloat) -> bool:
        return self._value >= float(other)

    # math
    def __iadd__(self, other: SupportsFloat):
        self._value += float(other)
        return self

    def __isub__(self, other: SupportsFloat):
        self._value -= float(other)
        return self

    def __imul__(self, other: SupportsFloat):
        self._value *= float(other)
        return self

    def __idiv__(self, other: SupportsFloat):
        self._value /= float(other)
        return self

    def __repr__(self) -> str:
        return repr(self._value)

    def __float__(self) -> float:
        return self._value

    def set(self, value: SupportsFloat) -> None:
        self._value = float(value)

    def scale(self, factor: float) -> int:
        self *= factor
        return 1

    def accept(self):
        self._backup = self._value

    def reject(self):
        self._value = self._backup
#

Epoch-versioned objects for use with MCMC

All stateful objects handled by MCMC must conform to this protocol. During each step operators can edit objects using whatever APIs provided. Then, at the end of the step, MCMC calls accept if the move has been accepted, or reject otherwise.

def set(self, value: ) -> None

    def set(self, value: SupportsFloat) -> None:
        self._value = float(value)
#

def scale(self, factor: float) -> int

    def scale(self, factor: float) -> int:
        self *= factor
        return 1
#

Scales all values of a parameter and returns the number of dimensions.

def accept(self)

    def accept(self):
        self._backup = self._value
#

Accept changes made during the current step

def reject(self)

    def reject(self):
        self._value = self._backup
#

Reject changes made during the current step

This method must roll the state of the object back to how it was at the beginning of the MCMC step.

class Weights:

class Weights(Stateful, Scalable):
    __slots__ = ("_value", "_backup", "_length")
    _value: list[float]
    _backup: list[float]
    _length: int

    def __init__(self, *args: float):
        self._value = list(args)
        self._backup = deepcopy(self._value)
        self._length = len(self._value)

    def __len__(self) -> int:
        return self._length

    def __getitem__(self, i: int) -> float:
        return self._value[i]

    def __setitem__(self, i: int, value: float):
        self._value[i] = value

    def __iter__(self):
        return iter(self._value)

    def __repr__(self) -> str:
        return repr(self._value)

    # comparison
    # XXX: deduplicate?
    def __lt__(self, other: SupportsFloat) -> bool:
        other = float(other)
        for item in self:
            if not item < other:
                return False
        return True

    def __le__(self, other: SupportsFloat) -> bool:
        other = float(other)
        for item in self:
            if not item <= other:
                return False
        return True

    def __gt__(self, other: SupportsFloat) -> bool:
        other = float(other)
        for item in self:
            if not item > other:
                return False
        return True

    def __eq__(self, other: object) -> bool:
        if isinstance(other, Weights):
            return self._value == other._value
        else:
            return False

    def __ne__(self, other: object) -> bool:
        if isinstance(other, Weights):
            return self._value != other._value
        else:
            return False

    def __ge__(self, other: SupportsFloat) -> bool:
        other = float(other)
        for item in self:
            if not item >= other:
                return False
        return True

    def scale(self, factor: float) -> int:
        for i in range(len(self)):
            self[i] *= factor
        return len(self)

    def accept(self):
        for i in range(len(self)):
            self._backup[i] = self._value[i]

    def reject(self):
        for i in range(len(self)):
            self._value[i] = self._backup[i]
#

Epoch-versioned objects for use with MCMC

All stateful objects handled by MCMC must conform to this protocol. During each step operators can edit objects using whatever APIs provided. Then, at the end of the step, MCMC calls accept if the move has been accepted, or reject otherwise.

def scale(self, factor: float) -> int

    def scale(self, factor: float) -> int:
        for i in range(len(self)):
            self[i] *= factor
        return len(self)
#

Scales all values of a parameter and returns the number of dimensions.

def accept(self)

    def accept(self):
        for i in range(len(self)):
            self._backup[i] = self._value[i]
#

Accept changes made during the current step

def reject(self)

    def reject(self):
        for i in range(len(self)):
            self._value[i] = self._backup[i]
#

Reject changes made during the current step

This method must roll the state of the object back to how it was at the beginning of the MCMC step.

class Internals:

@dataclass
class Internals(Scalable):
    tree: Tree

    def scale(self, factor: float) -> int:
        self.tree.scale(factor)

        return self.tree.num_internals
#

tree: aspartik.b3.Tree

#

def scale(self, factor: float) -> int

    def scale(self, factor: float) -> int:
        self.tree.scale(factor)

        return self.tree.num_internals
#

Scales all values of a parameter and returns the number of dimensions.

class Root:

@dataclass
class Root:
    tree: Tree

    def __float__(self) -> float:
        return self.tree.height_of(self.tree.root)
#

tree: aspartik.b3.Tree

#