Source code for pytomography.likelihoods.likelihood

from __future__ import annotations
import pytomography
from pytomography.projectors import SystemMatrix
from collections.abc import Callable
import torch

[docs]class Likelihood: """Generic likelihood class in PyTomography. Subclasses may implement specific likelihoods with methods to compute the likelihood itself as well as particular gradients of the likelihood Args: system_matrix (SystemMatrix): The system matrix modeling the particular system whereby the projections were obtained projections (torch.Tensor | None): Acquired data. If listmode, then this argument need not be provided, and it is set to a tensor of ones. Defaults to None. additive_term (torch.Tensor, optional): Additional term added after forward projection by the system matrix. This term might include things like scatter and randoms. Defaults to None. additive_term_variance_estimate (Callable, optional): Operator for variance estimate of additive term. If none, then uncertainty estimation does not include contribution from the additive term. Defaults to None. """ def __init__( self, system_matrix: SystemMatrix, projections: torch.Tensor | None = None, additive_term: torch.Tensor = None, additive_term_variance_estimate: Callable | None = None ) -> None: self.system_matrix = system_matrix if projections is None: # listmode reconstruction self.projections = torch.tensor([1.]).to(pytomography.device) else: self.projections = projections self.FP = None # stores current state of forward projection if type(additive_term) is torch.Tensor: self.additive_term = additive_term.to(self.projections.device).to(pytomography.dtype) self.exists_additive_term = True else: self.additive_term = torch.zeros(self.projections.shape).to(self.projections.device).to(pytomography.dtype) self.exists_additive_term = False self.n_subsets_previous = -1 self.additive_term_variance_estimate = additive_term_variance_estimate
[docs] def _set_n_subsets( self, n_subsets: int )-> None: """Sets the number of subsets to be used when computing the likelihood Args: n_subsets (int): Number of subsets """ self.n_subsets = n_subsets if n_subsets < 2: self.norm_BP = self.system_matrix.compute_normalization_factor() else: self.system_matrix.set_n_subsets(n_subsets) if self.n_subsets_previous!=self.n_subsets: self.norm_BPs = [] for k in range(self.n_subsets): self.norm_BPs.append(self.system_matrix.compute_normalization_factor(k)) self.n_subsets_previous = n_subsets
[docs] def _get_projection_subset(self, projections: torch.Tensor, subset_idx: int | None = None) -> torch.Tensor: """Method for getting projection subset corresponding to given subset index Args: projections (torch.Tensor): Projection data subset_idx (int): Subset index Returns: torch.Tensor: Subset projection data """ if subset_idx is None: return projections else: return self.system_matrix.get_projection_subset(projections, subset_idx)
[docs] def _get_normBP(self, subset_idx: int, return_sum: bool = False): """Gets normalization factor (back projection of ones) Args: subset_idx (int): Subset index return_sum (bool, optional): Sum normalization factor from all subsets. Defaults to False. Returns: torch.Tensor: Normalization factor """ if subset_idx is None: return self.norm_BP else: if return_sum: return torch.stack(self.norm_BPs).sum(axis=0) else: # Put on PyTomography device in case stored on CPU return self.norm_BPs[subset_idx].to(pytomography.device)
[docs] def compute_gradient(self, *args, **kwargs): r"""Function used to compute the gradient of the likelihood :math:`\nabla_{f} L(g|f)` Raises: NotImplementedError: Must be implemented by sub classes """ raise NotImplementedError("Compute gradient not implemented for this likelihood function")
[docs] def compute_gradient_ff(self, *args, **kwargs): r"""Function used to compute the second order gradient (with respect to the object twice) of the likelihood :math:`\nabla_{ff} L(g|f)` Raises: NotImplementedError: Must be implemented by sub classes """ raise NotImplementedError("gradient_ff not implemented for this likelihood function")
[docs] def compute_gradient_gf(self, *args, **kwargs): r"""Function used to compute the second order gradient (with respect to the object then image) of the likelihood :math:`\nabla_{gf} L(g|f)` Raises: NotImplementedError: Must be implemented by sub classes """ raise NotImplementedError("gradient_gf not implemented for this likelihood function")
[docs] def compute_gradient_sf(self, *args, **kwargs): r"""Function used to compute the second order gradient (with respect to the object then additive term) of the likelihood :math:`\nabla_{sf} L(g|f,s)` Raises: NotImplementedError: Must be implemented by sub classes """ raise NotImplementedError("gradient_sf not implemented for this likelihood function")