Source code for odatse.util.mapping

# SPDX-License-Identifier: MPL-2.0
#
# ODAT-SE -- an open framework for data analysis
# Copyright (C) 2020- The University of Tokyo
#
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

import copy
import numpy as np

from .read_matrix import read_matrix

# type hints
from typing import Optional

[docs] class MappingBase: """ Base class for mapping operations. """ def __init__(self): pass def __call__(self, x: np.ndarray) -> np.ndarray: """ Apply the mapping to the input array. Parameters ---------- x : np.ndarray Input array. Returns ------- np.ndarray Mapped array. """ raise NotImplementedError
[docs] class TrivialMapping(MappingBase): """ A trivial mapping that returns the input array unchanged. """ def __init__(self): super().__init__() def __call__(self, x: np.ndarray) -> np.ndarray: """ Return the input array unchanged. Parameters ---------- x : np.ndarray Input array. Returns ------- np.ndarray The same input array. """ return x
[docs] class Affine(MappingBase): """ An affine mapping defined by a matrix A and a vector b. """ A: Optional[np.ndarray] b: Optional[np.ndarray]
[docs] def __init__(self, A: Optional[np.ndarray] = None, b: Optional[np.ndarray] = None): """ Initialize the affine mapping. Parameters ---------- A : np.ndarray, optional Transformation matrix. b : np.ndarray, optional Translation vector. """ # copy arguments self.A = np.array(A) if A is not None else None self.b = np.array(b) if b is not None else None # check if self.A is not None: if not self.A.ndim == 2: raise ValueError("A is not a matrix") if self.b is not None: if not self.b.ndim == 1: raise ValueError("b is not a vector") if self.A is not None and self.b is not None: if not self.A.shape[0] == self.b.shape[0]: raise ValueError("shape of A and b mismatch")
def __call__(self, x: np.ndarray) -> np.ndarray: """ Apply the affine mapping to the input array. Parameters ---------- x : np.ndarray Input array. Returns ------- np.ndarray Mapped array. """ if self.A is None: ret = copy.copy(x) else: ret = np.dot(self.A, x) if self.b is None: return ret else: return ret + self.b
[docs] @classmethod def from_dict(cls, d): """ Create an Affine instance from a dictionary. Parameters ---------- d : dict Dictionary containing 'A' and 'b' keys. Returns ------- Affine An instance of the Affine class. """ A: Optional[np.ndarray] = read_matrix(d.get("A", [])) b: Optional[np.ndarray] = read_matrix(d.get("b", [])) if A is None: pass elif A.size == 0: A = None else: if not A.ndim == 2: raise ValueError("A should be a matrix") if b is None: pass elif b.size == 0: b = None else: if not (b.ndim == 2 and b.shape[1] == 1): raise ValueError("b should be a column vector") if not (A is not None and b.shape[0] == A.shape[0]): raise ValueError("shape of A and b mismatch") b = b.reshape(-1) return cls(A, b)