Source code for torch_openreml.covariance.operator_sum

"""
Sum covariance operator.

This module provides a Sum operator for combining multiple covariance
matrices additively, typically used to represent multi-component variance
structures in linear mixed-effects models.

Classes:
    Sum:
        A covariance operator representing
        :math:`V = \\sum_i A_i`, where all operands share the same shape.
"""

from torch_openreml.covariance.operator import Operator
import torch

[docs] class Sum(Operator): r""" Sum of multiple covariance matrices. .. math:: \symbf{V} = \sum_{i=1}^{k} \symbf{A}_i where each :math:`\symbf{A}_i` is a covariance matrix of the same shape. This operator represents additive covariance structures, commonly used in linear mixed-effects models to combine multiple variance components (e.g., genetic, environmental, and residual). All operands must evaluate to matrices of identical shape. Each operand may be a trainable :class:`~torch_openreml.covariance.matrix.Matrix` or a fixed :class:`torch.Tensor`. """ def __init__(self, *args, **kwargs): """ Initialize a sum operator from two or more operands. Args: *args: Two or more operands as positional arguments or a single dict mapping names to operands. **kwargs: Two or more operands as keyword arguments. Raises: ValueError: If fewer than two operands are provided. Example: .. jupyter-execute:: import torch from torch_openreml.covariance import AR1Matrix, ScalarMatrix, Sum op = Sum(time=AR1Matrix(4), noise=ScalarMatrix(4)) free_params = torch.tensor([0.5, 1.0, 1.0]) op(free_params) """ super().__init__(*args, **kwargs) if len(self.operands) < 2: raise ValueError("At least two operands are required")
[docs] def __call__(self, free_params=None): if free_params is None: free_params = self.free_param_defaults v_groups = self.build_operands(free_params) v = sum(v_groups) self._shape = tuple(v.shape) return v
[docs] def manual_grad(self, free_params=None): """ Compute the Jacobian of :meth:`__call__` with respect to trainable parameters using a closed-form analytic expression. Since :math:`\\symbf{V} = \\sum_i \\symbf{A}_i`, the gradient with respect to each operand's parameters is simply the operand's own gradient — there is no cross-term interaction. Per-operand Jacobians from :meth:`~torch_openreml.covariance.operator.Operator.operands_grad` are concatenated directly. Args: free_params (torch.Tensor or dict): Flat 1D parameter tensor or parameter dictionary. If omitted, default values are used. Default: ``None``. Returns: tuple: ``(grad, grad_names)``, where ``grad`` is a 3D tensor of shape ``(num_free_params, *shape)`` and ``grad_names`` is a list of the corresponding parameter names. Returns ``(None, [])`` if all parameters are fixed. Raises: TypeError: If ``free_params`` is not a Torch tensor. ValueError: If ``free_params`` is not a 1D tensor or has the wrong length, or if ``free_params`` is a dict with missing or unexpected keys. Example: .. jupyter-execute:: import torch from torch_openreml.covariance import AR1Matrix, ScalarMatrix, Sum op = Sum(time=AR1Matrix(4), noise=ScalarMatrix(4)) free_params = torch.tensor([0.5, 1.0, 1.0]) grad, grad_names = op.manual_grad(free_params) grad .. jupyter-execute:: grad_names """ if free_params is None: free_params = self.free_param_defaults grad_groups, grad_name_groups = self.operands_grad(free_params) grad_groups = [grad for grad in grad_groups if grad is not None] if len(grad_groups) > 0: grad = torch.cat(grad_groups) grad_names = [name for group in grad_name_groups for name in group] return grad, grad_names else: return None, []