romtools.vector_space.utils.shifter
Notes
The vector defining the affine offset for a linear subspace is viewed as a matrix of shape $$\mathbf{u}_{\mathrm{shift}} \in \mathbb{R}^{N_{\mathrm{vars}} \times N_{\mathrm{x}} }$$
Theory
What is a shift vector, and why would I use it? In ROMs, we restrict a state to belong to a low-dimensional affine vector space, $$\mathbf{u} \approx \tilde{\mathbf{u}} \in \mathcal{V} + \mathbf{u}_{\mathrm{shift}}$$ where $\mathcal{V} \equiv \mathrm{range}(\boldsymbol \Phi) $. Here $\mathbf{u}_{\mathrm{shift}}$ defines an affine offset. Affine offsets can be useful for a variety of reasons, including satisfying boundary conditions, and satisfying initial conditions.
The _Shifter class encapsulates the affine offset.
API
1# 2# ************************************************************************ 3# 4# ROM Tools and Workflows 5# Copyright 2019 National Technology & Engineering Solutions of Sandia,LLC 6# (NTESS) 7# 8# Under the terms of Contract DE-NA0003525 with NTESS, the 9# U.S. Government retains certain rights in this software. 10# 11# ROM Tools and Workflows is licensed under BSD-3-Clause terms of use: 12# 13# Redistribution and use in source and binary forms, with or without 14# modification, are permitted provided that the following conditions 15# are met: 16# 17# 1. Redistributions of source code must retain the above copyright 18# notice, this list of conditions and the following disclaimer. 19# 20# 2. Redistributions in binary form must reproduce the above copyright 21# notice, this list of conditions and the following disclaimer in the 22# documentation and/or other materials provided with the distribution. 23# 24# 3. Neither the name of the copyright holder nor the names of its 25# contributors may be used to endorse or promote products derived 26# from this software without specific prior written permission. 27# 28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 31# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 32# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 33# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 34# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 35# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 37# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 38# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39# POSSIBILITY OF SUCH DAMAGE. 40# 41# Questions? Contact Eric Parish (ejparis@sandia.gov) 42# 43# ************************************************************************ 44# 45 46''' 47___ 48##**Notes** 49The vector defining the affine offset for a linear subspace is viewed as a matrix of shape 50$$\\mathbf{u}_{\\mathrm{shift}} \in \\mathbb{R}^{N_{\\mathrm{vars}} \\times N_{\mathrm{x}} }$$ 51 52___ 53##**Theory** 54 55*What is a shift vector, and why would I use it?* In ROMs, we restrict a state to belong to a low-dimensional affine 56vector space, 57$$\\mathbf{u} \\approx \\tilde{\\mathbf{u}} \\in \\mathcal{V} + \\mathbf{u}_{\\mathrm{shift}}$$ 58where 59$\\mathcal{V} \\equiv \\mathrm{range}(\\boldsymbol \\Phi) $. Here $\\mathbf{u}_{\\mathrm{shift}}$ defines an affine offset. 60Affine offsets can be useful for a variety of reasons, including satisfying boundary conditions, and satisfying initial 61conditions. 62 63The _Shifter class encapsulates the affine offset. 64 65___ 66##**API** 67''' 68 69import sys 70from numbers import Number 71from typing import Protocol 72import numpy as np 73import romtools.linalg.linalg as la 74 75 76class Shifter(Protocol): 77 '''Interface for the Shifter class.''' 78 def apply_shift(self, my_array: np.ndarray) -> None: 79 '''Shifts the snapshot matrix by subtracting a vector generated by the public-facing free functions.''' 80 ... 81 82 def apply_inverse_shift(self, my_array: np.ndarray) -> None: 83 '''Shifts the snapshot matrix by adding a vector generated by the public-facing free functions.''' 84 ... 85 86 def get_shift_vector(self) -> np.ndarray: 87 '''Returns the vector used to shift the data.''' 88 ... 89 90 91class _Shifter(): 92 ''' 93 Shifts the data by a vector generated by the public-facing free functions. 94 95 This class conforms to `Shifter` protocol. 96 ''' 97 def __init__(self, shift_vector: np.ndarray) -> None: 98 ''' 99 Constructor 100 101 Args: 102 shift_vector (np.ndarray): The vector to shift the data by. 103 ''' 104 self.__shift_vector = shift_vector.copy() 105 106 def apply_shift(self, my_array: np.ndarray) -> None: 107 '''Shifts the input array in place by subtracting the provided shift vector.''' 108 my_array -= self.__shift_vector[..., None] 109 110 def apply_inverse_shift(self, my_array: np.ndarray) -> None: 111 '''Shifts the input array in place by adding the provided shift vector.''' 112 my_array += self.__shift_vector[..., None] 113 114 def get_shift_vector(self) -> np.ndarray: 115 '''Returns the shift vector.''' 116 return self.__shift_vector 117 118 119def create_noop_shifter(my_array: np.ndarray) -> Shifter: 120 '''No op implementation.''' 121 shift_vector = np.zeros((my_array.shape[0], my_array.shape[1])) 122 shifter = _Shifter(shift_vector) 123 return shifter 124 125 126def create_constant_shifter(shift_value, my_array: np.ndarray) -> Shifter: 127 '''Shifts the data by a constant value.''' 128 if isinstance(shift_value, np.ndarray): 129 shift_vector = np.empty((my_array.shape[0], my_array.shape[1],)) 130 assert my_array.shape[0] == shift_value.size 131 for i in range(0, my_array.shape[0]): 132 shift_vector[i] = shift_value[i] 133 elif isinstance(shift_value, Number): 134 shift_vector = np.full((my_array.shape[0], my_array.shape[1],), shift_value) 135 else: 136 sys.exit("Error: shift_value must be either a number or np.ndarray.") 137 shifter = _Shifter(shift_vector) 138 return shifter 139 140 141def create_vector_shifter(shift_vector: np.ndarray) -> Shifter: 142 '''Shifts the data by a user-input vector.''' 143 shifter = _Shifter(shift_vector) 144 return shifter 145 146 147def create_average_shifter(my_array: np.ndarray) -> Shifter: 148 '''Shifts the data by the average of a data matrix.''' 149 shift_vector = la.mean(my_array, axis=2) 150 return _Shifter(shift_vector) 151 152 153def create_firstvec_shifter(my_array: np.ndarray) -> Shifter: 154 '''Shifts the data by the first vector of a data matrix.''' 155 shift_vector = my_array[:, :, 0] 156 shifter = _Shifter(shift_vector) 157 return shifter
77class Shifter(Protocol): 78 '''Interface for the Shifter class.''' 79 def apply_shift(self, my_array: np.ndarray) -> None: 80 '''Shifts the snapshot matrix by subtracting a vector generated by the public-facing free functions.''' 81 ... 82 83 def apply_inverse_shift(self, my_array: np.ndarray) -> None: 84 '''Shifts the snapshot matrix by adding a vector generated by the public-facing free functions.''' 85 ... 86 87 def get_shift_vector(self) -> np.ndarray: 88 '''Returns the vector used to shift the data.''' 89 ...
Interface for the Shifter class.
1771def _no_init_or_replace_init(self, *args, **kwargs): 1772 cls = type(self) 1773 1774 if cls._is_protocol: 1775 raise TypeError('Protocols cannot be instantiated') 1776 1777 # Already using a custom `__init__`. No need to calculate correct 1778 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1779 if cls.__init__ is not _no_init_or_replace_init: 1780 return 1781 1782 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1783 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1784 # searches for a proper new `__init__` in the MRO. The new `__init__` 1785 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1786 # instantiation of the protocol subclass will thus use the new 1787 # `__init__` and no longer call `_no_init_or_replace_init`. 1788 for base in cls.__mro__: 1789 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1790 if init is not _no_init_or_replace_init: 1791 cls.__init__ = init 1792 break 1793 else: 1794 # should not happen 1795 cls.__init__ = object.__init__ 1796 1797 cls.__init__(self, *args, **kwargs)
79 def apply_shift(self, my_array: np.ndarray) -> None: 80 '''Shifts the snapshot matrix by subtracting a vector generated by the public-facing free functions.''' 81 ...
Shifts the snapshot matrix by subtracting a vector generated by the public-facing free functions.
83 def apply_inverse_shift(self, my_array: np.ndarray) -> None: 84 '''Shifts the snapshot matrix by adding a vector generated by the public-facing free functions.''' 85 ...
Shifts the snapshot matrix by adding a vector generated by the public-facing free functions.
120def create_noop_shifter(my_array: np.ndarray) -> Shifter: 121 '''No op implementation.''' 122 shift_vector = np.zeros((my_array.shape[0], my_array.shape[1])) 123 shifter = _Shifter(shift_vector) 124 return shifter
No op implementation.
127def create_constant_shifter(shift_value, my_array: np.ndarray) -> Shifter: 128 '''Shifts the data by a constant value.''' 129 if isinstance(shift_value, np.ndarray): 130 shift_vector = np.empty((my_array.shape[0], my_array.shape[1],)) 131 assert my_array.shape[0] == shift_value.size 132 for i in range(0, my_array.shape[0]): 133 shift_vector[i] = shift_value[i] 134 elif isinstance(shift_value, Number): 135 shift_vector = np.full((my_array.shape[0], my_array.shape[1],), shift_value) 136 else: 137 sys.exit("Error: shift_value must be either a number or np.ndarray.") 138 shifter = _Shifter(shift_vector) 139 return shifter
Shifts the data by a constant value.
142def create_vector_shifter(shift_vector: np.ndarray) -> Shifter: 143 '''Shifts the data by a user-input vector.''' 144 shifter = _Shifter(shift_vector) 145 return shifter
Shifts the data by a user-input vector.
148def create_average_shifter(my_array: np.ndarray) -> Shifter: 149 '''Shifts the data by the average of a data matrix.''' 150 shift_vector = la.mean(my_array, axis=2) 151 return _Shifter(shift_vector)
Shifts the data by the average of a data matrix.
154def create_firstvec_shifter(my_array: np.ndarray) -> Shifter: 155 '''Shifts the data by the first vector of a data matrix.''' 156 shift_vector = my_array[:, :, 0] 157 shifter = _Shifter(shift_vector) 158 return shifter
Shifts the data by the first vector of a data matrix.