Source code for pysiglib.logsig_to_sig_backprop

# Copyright 2026 Daniil Shmelev
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =========================================================================

from typing import Union

import numpy as np
import torch

from .param_checks import check_type, check_non_neg, check_n_jobs
from .error_codes import err_msg
from .dtypes import (CPSIG_LOGSIG_TO_SIG_BACKPROP,
                     CUSIG_LOGSIG_TO_SIG_BACKPROP_CUDA)
from .sig_length import sig_length, log_sig_length, aug_dim, _infer_scalar_term
from .data_handlers import SigOutputHandler, SigInputHandler


[docs] def logsig_to_sig_backprop( log_sig : Union[np.ndarray, torch.tensor], sig_derivs : Union[np.ndarray, torch.tensor], dimension : int, degree : int, *, time_aug : bool = False, lead_lag : bool = False, method : int = 1, scalar_term : bool = False, n_jobs : int = 1 ) -> Union[np.ndarray, torch.tensor]: """ Backpropagation through :func:`pysiglib.logsig_to_sig`. Given upstream derivatives ``sig_derivs`` (dL/d(sig)), computes the gradient dL/d(log_sig). Supports all methods (``0``, ``1``, ``2``). :param log_sig: The log-signature used in the forward pass. :type log_sig: numpy.ndarray | torch.tensor :param sig_derivs: Upstream derivatives dL/d(sig), same shape as the signature output. :type sig_derivs: numpy.ndarray | torch.tensor :param dimension: Dimension of the underlying path(s). :type dimension: int :param degree: Truncation degree. :type degree: int :param time_aug: Whether the signatures were computed with ``time_aug=True``. :type time_aug: bool :param lead_lag: Whether the signatures were computed with ``lead_lag=True``. :type lead_lag: bool :param method: Method to use (``0``, ``1``, or ``2``). Must match the method used in the forward pass. :type method: int :param scalar_term: For methods ``1`` and ``2``, whether ``sig_derivs`` includes the leading constant 1 at index 0 and the returned gradient should too. Ignored for method ``0``: the format is inferred from the input ``log_sig`` (which is sig-shaped for method ``0``) and the returned gradient matches it. :type scalar_term: bool :param n_jobs: Number of threads to run in parallel. :type n_jobs: int :return: Gradient dL/d(log_sig), same shape as ``log_sig``. :rtype: numpy.ndarray | torch.tensor """ check_type(dimension, "dimension", int) check_non_neg(dimension, "dimension") check_type(degree, "degree", int) check_non_neg(degree, "degree") check_type(time_aug, "time_aug", bool) check_type(lead_lag, "lead_lag", bool) check_type(method, "method", int) if method not in (0, 1, 2): raise ValueError("method must be 0, 1, or 2") aug_dimension = aug_dim(dimension, time_aug, lead_lag) # Method 0: log_sig is sig-shaped, auto-detect; output gradient matches input log_sig format. # Methods 1, 2: log_sig is log-sig-shaped (no scalar_term concept), scalar_term controls sig_derivs/output. if method == 0: scalar_term = _infer_scalar_term(log_sig, dimension, degree, time_aug=time_aug, lead_lag=lead_lag) input_len = sig_length(aug_dimension, degree, scalar_term=scalar_term) else: input_len = log_sig_length(aug_dimension, degree) sig_len = sig_length(aug_dimension, degree, scalar_term=scalar_term) data = SigInputHandler(log_sig, input_len, "log_sig") derivs_data = SigInputHandler(sig_derivs, sig_len, "sig_derivs") result = SigOutputHandler(data, input_len) if data.batch_size == 0: return result.data check_n_jobs(n_jobs) if data.device == "cpu": err_code = CPSIG_LOGSIG_TO_SIG_BACKPROP[data.dtype]( data.data_ptr, result.data_ptr, derivs_data.data_ptr, data.batch_size, dimension, degree, time_aug, lead_lag, method, scalar_term, n_jobs) else: err_code = CUSIG_LOGSIG_TO_SIG_BACKPROP_CUDA[data.dtype]( data.data_ptr, result.data_ptr, derivs_data.data_ptr, data.batch_size, aug_dimension, degree, method, scalar_term) if err_code: raise Exception("Error in pysiglib.logsig_to_sig_backprop: " + err_msg(err_code)) return result.data