aspartik.b3.callbacks

class PrintLogger:

@dataclass(slots=True)
class PrintLogger(Callback):
    """
    Prints the simulation progress onto the screen

    Currently it only supports the step index, posterior/likelihood/total
    prior, and speed in time per million steps.
    """

    every: int

    _last_time: Optional[float] = field(init=False, default=None)

    def call(self, mcmc: MCMC):
        if mcmc.current_step == 0:
            print(
                f"{'Step':>10}{'Posterior':>15}{'Likelihood':>15}{'Prior':>15}{'Speed t/m':>15}"
            )

        current_time = time.perf_counter()
        if self._last_time:
            # in seconds
            speed = (current_time - self._last_time) / self.every * 1_000_000

            speed = f"{speed / 60:.1f}min" if speed >= 60 else f"{speed:.0f}sec"
        else:
            speed = "-"

        likelihood = mcmc.likelihood.likelihood()
        print(
            f"{mcmc.current_step:>10}{mcmc.posterior:>15.2f}{likelihood:>15.2f}{mcmc.prior:>15.2f}{speed:>15}"
        )

        self._last_time = current_time
#

Prints the simulation progress onto the screen

Currently it only supports the step index, posterior/likelihood/total prior, and speed in time per million steps.

every: int

#

How often this callback should be called

The MCMC will call each callback object when index % every is 0. This value is read once when MCMC is created, so if it's changed during execution, the old every value will continue to be used.

def call(self, mcmc: aspartik.b3.MCMC)

    def call(self, mcmc: MCMC):
        if mcmc.current_step == 0:
            print(
                f"{'Step':>10}{'Posterior':>15}{'Likelihood':>15}{'Prior':>15}{'Speed t/m':>15}"
            )

        current_time = time.perf_counter()
        if self._last_time:
            # in seconds
            speed = (current_time - self._last_time) / self.every * 1_000_000

            speed = f"{speed / 60:.1f}min" if speed >= 60 else f"{speed:.0f}sec"
        else:
            speed = "-"

        likelihood = mcmc.likelihood.likelihood()
        print(
            f"{mcmc.current_step:>10}{mcmc.posterior:>15.2f}{likelihood:>15.2f}{mcmc.prior:>15.2f}{speed:>15}"
        )

        self._last_time = current_time
#

A custom operation

Used by loggers and other periodic actions.

def finish(self, mcmc: aspartik.b3.MCMC) -> None

    def finish(self, mcmc: MCMC) -> None:
        """
        A method which will be called after a run ends

        It's useful for finalizing logger actions, including flushing to files.
        This method has a default no-op implementation, so it's not necessary
        to implement it on a custom logger.
        """
        pass
#

A method which will be called after a run ends

It's useful for finalizing logger actions, including flushing to files. This method has a default no-op implementation, so it's not necessary to implement it on a custom logger.

class TreeValidator:

@dataclass(slots=True)
class TreeValidator(Callback):
    """
    Checks that the trees it passed to it are valid

    This callback simply calls the `validate` method on all trees it tracks.
    By default it is called on every step, which will slow the analysis down
    considerably on large trees.  This callback is mostly intended for debug
    purposes when developing new operators.
    """

    trees: tuple[Tree]
    """An iterable of trees to validate"""
    every: int = 1

    def call(self, mcmc: MCMC) -> None:
        for tree in self.trees:
            tree.validate()
#

Checks that the trees it passed to it are valid

This callback simply calls the validate method on all trees it tracks. By default it is called on every step, which will slow the analysis down considerably on large trees. This callback is mostly intended for debug purposes when developing new operators.

trees: tuple

#

An iterable of trees to validate

every: int

#

How often this callback should be called

The MCMC will call each callback object when index % every is 0. This value is read once when MCMC is created, so if it's changed during execution, the old every value will continue to be used.

def call(self, mcmc: aspartik.b3.MCMC) -> None

    def call(self, mcmc: MCMC) -> None:
        for tree in self.trees:
            tree.validate()
#

A custom operation

Used by loggers and other periodic actions.

def finish(self, mcmc: aspartik.b3.MCMC) -> None

    def finish(self, mcmc: MCMC) -> None:
        """
        A method which will be called after a run ends

        It's useful for finalizing logger actions, including flushing to files.
        This method has a default no-op implementation, so it's not necessary
        to implement it on a custom logger.
        """
        pass
#

A method which will be called after a run ends

It's useful for finalizing logger actions, including flushing to files. This method has a default no-op implementation, so it's not necessary to implement it on a custom logger.

class Timer:

@dataclass(slots=True)
class Timer(Callback):
    """
    Prints the total execution time of an MCMC run
    """

    __start: float = field(init=False, default=0.0)
    __start_index: int = field(init=False)
    every: int = field(init=False, default=2**32)

    def call(self, mcmc: MCMC) -> None:
        "Due to high `every` this method will only be called once on step 0"

        if self.__start == 0.0:
            self.__start = time.perf_counter()
            self.__start_index = mcmc.current_step

    def finish(self, mcmc: MCMC) -> None:
        if not self.__start:
            return

        duration = time.perf_counter() - self.__start
        speed = duration / (mcmc.current_step - self.__start_index) * 1_000_000
        print(f"Timer: {speed} sec/million steps ({duration} sec total)")
#

Prints the total execution time of an MCMC run

every: int

#

How often this callback should be called

The MCMC will call each callback object when index % every is 0. This value is read once when MCMC is created, so if it's changed during execution, the old every value will continue to be used.

def call(self, mcmc: aspartik.b3.MCMC) -> None

    def call(self, mcmc: MCMC) -> None:
        "Due to high `every` this method will only be called once on step 0"

        if self.__start == 0.0:
            self.__start = time.perf_counter()
            self.__start_index = mcmc.current_step
#

Due to high every this method will only be called once on step 0

def finish(self, mcmc: aspartik.b3.MCMC) -> None

    def finish(self, mcmc: MCMC) -> None:
        if not self.__start:
            return

        duration = time.perf_counter() - self.__start
        speed = duration / (mcmc.current_step - self.__start_index) * 1_000_000
        print(f"Timer: {speed} sec/million steps ({duration} sec total)")
#

A method which will be called after a run ends

It's useful for finalizing logger actions, including flushing to files. This method has a default no-op implementation, so it's not necessary to implement it on a custom logger.

class StateCheckpoint:

@dataclass(slots=True)
class StateCheckpoint(Callback):
    """
    Saves the MCMC state
    """

    path: str
    "Path to the file to save the state in"

    every: int

    def save_state(self, mcmc: MCMC):
        with open(self.path, "wb") as file:
            file.write(mcmc.dump_state())

    def call(self, mcmc: MCMC) -> None:
        self.save_state(mcmc)

    def finish(self, mcmc: MCMC) -> None:
        self.save_state(mcmc)
#

Saves the MCMC state

path: str

#

Path to the file to save the state in

every: int

#

How often this callback should be called

The MCMC will call each callback object when index % every is 0. This value is read once when MCMC is created, so if it's changed during execution, the old every value will continue to be used.

def save_state(self, mcmc: aspartik.b3.MCMC)

    def save_state(self, mcmc: MCMC):
        with open(self.path, "wb") as file:
            file.write(mcmc.dump_state())
#

def call(self, mcmc: aspartik.b3.MCMC) -> None

    def call(self, mcmc: MCMC) -> None:
        self.save_state(mcmc)
#

A custom operation

Used by loggers and other periodic actions.

def finish(self, mcmc: aspartik.b3.MCMC) -> None

    def finish(self, mcmc: MCMC) -> None:
        self.save_state(mcmc)
#

A method which will be called after a run ends

It's useful for finalizing logger actions, including flushing to files. This method has a default no-op implementation, so it's not necessary to implement it on a custom logger.

class TraceWriter:

#

every

#

def call(self, /, mcmc)

#

The type of the None singleton.

def finish(self, /, _mcmc)

#

The type of the None singleton.