GitHub

romtools.vector_space.utils.svd_method_of_snapshots

  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#
 45from typing import Any, Tuple
 46import numpy as np
 47import romtools.linalg.linalg as la
 48
 49
 50class SvdMethodOfSnapshots:
 51    '''
 52    #Parallel implementation of the method of snapshots to mimic the SVD for basis construction
 53    Sample usage:
 54
 55                  mySvd = SvdMethodOfSnapshots(comm)
 56                  U,s,_ = mySvd(snapshots)
 57
 58    where snapshots is the local portion of a distributed memory array.
 59
 60    The standard reduced-basis problem requires solving the optimization problem
 61    $$ \\boldsymbol \\Phi = \\underset{ \\boldsymbol \\Phi_{\\*} \\in \\mathbb{R}^{N \\times K} | \\boldsymbol
 62    \\Phi_{\\*}^T \\boldsymbol \\Phi_{\\*} = \\mathbf{I}}{ \\mathrm{arg \\; min} } \\| \\Phi_{\\*} \\Phi_{\\*}^T
 63    \\mathbf{S} - \\mathbf{S} \\|_2,$$
 64    where $\\mathbf{S} \\in \\mathbb{R}^{N \\times N_s}$, with $N_s$ being the number of snapshots.
 65    The standard way to solve this is with the thin SVD. An alternative approach is to use the method of
 66    snapshts/kernel trick, see, e.g., https://web.stanford.edu/group/frg/course_work/CME345/CA-CME345-Ch4.pdf.
 67    Here, we instead solve the eigenvalue probelm
 68    $$ \\mathbf{S}^T \\mathbf{S} \\boldsymbol \\psi_i = \\lambda_i \\boldsymbol \\psi_i$$
 69    for $i = 1,\\ldots,N_s$. It can be shown that the left singular vectors from the SVD of $\\mathbf{S}$ are
 70    related to the eigen-vectors of the above by
 71    $$ \\mathbf{u}_i = \\frac{1}{\\sqrt{\\lambda_i}} \\mathbf{S} \\boldsymbol \\psi_i.$$
 72
 73    An advantage of the method of snapshots is that it can be easily parallelized and is efficient if we don't
 74    have many snapshots. We compute $\\mathbf{S}^T \\mathbf{S}$ in parallel, and then solve the (typically small)
 75    eigenvalue problem in serial.
 76    '''
 77
 78    def __init__(self, comm) -> None:
 79        self._comm = comm
 80
 81    def __call__(self, snapshots: np.ndarray,
 82                 full_matrices: bool = False,
 83                 compute_uv: bool = False,
 84                 hermitian: bool = False) -> Tuple[np.ndarray, np.ndarray, Any]:
 85        U, s = la.thin_svd(snapshots, self._comm, method='method_of_snapshots')
 86        return U, s, 'not_computed_in_method_of_snapshots'
 87
 88
 89class SvdMethodOfSnapshotsForQr:
 90    '''
 91    Same as SvdMethodOfSnapshots, but call only returns two arguments to be
 92    compatible with QR routine.
 93    '''
 94    def __init__(self, comm) -> None:
 95        self._comm = comm
 96
 97    def __call__(self, snapshots: np.ndarray,
 98                 full_matrices: bool = False,
 99                 compute_uv: bool = False,
100                 hermitian: bool = False) -> Tuple[np.ndarray, Any]:
101        U, _ = la.thin_svd(snapshots, self._comm, method='method_of_snapshots')
102        return U, 'not_computed_in_method_of_snapshots'
class SvdMethodOfSnapshots:
51class SvdMethodOfSnapshots:
52    '''
53    #Parallel implementation of the method of snapshots to mimic the SVD for basis construction
54    Sample usage:
55
56                  mySvd = SvdMethodOfSnapshots(comm)
57                  U,s,_ = mySvd(snapshots)
58
59    where snapshots is the local portion of a distributed memory array.
60
61    The standard reduced-basis problem requires solving the optimization problem
62    $$ \\boldsymbol \\Phi = \\underset{ \\boldsymbol \\Phi_{\\*} \\in \\mathbb{R}^{N \\times K} | \\boldsymbol
63    \\Phi_{\\*}^T \\boldsymbol \\Phi_{\\*} = \\mathbf{I}}{ \\mathrm{arg \\; min} } \\| \\Phi_{\\*} \\Phi_{\\*}^T
64    \\mathbf{S} - \\mathbf{S} \\|_2,$$
65    where $\\mathbf{S} \\in \\mathbb{R}^{N \\times N_s}$, with $N_s$ being the number of snapshots.
66    The standard way to solve this is with the thin SVD. An alternative approach is to use the method of
67    snapshts/kernel trick, see, e.g., https://web.stanford.edu/group/frg/course_work/CME345/CA-CME345-Ch4.pdf.
68    Here, we instead solve the eigenvalue probelm
69    $$ \\mathbf{S}^T \\mathbf{S} \\boldsymbol \\psi_i = \\lambda_i \\boldsymbol \\psi_i$$
70    for $i = 1,\\ldots,N_s$. It can be shown that the left singular vectors from the SVD of $\\mathbf{S}$ are
71    related to the eigen-vectors of the above by
72    $$ \\mathbf{u}_i = \\frac{1}{\\sqrt{\\lambda_i}} \\mathbf{S} \\boldsymbol \\psi_i.$$
73
74    An advantage of the method of snapshots is that it can be easily parallelized and is efficient if we don't
75    have many snapshots. We compute $\\mathbf{S}^T \\mathbf{S}$ in parallel, and then solve the (typically small)
76    eigenvalue problem in serial.
77    '''
78
79    def __init__(self, comm) -> None:
80        self._comm = comm
81
82    def __call__(self, snapshots: np.ndarray,
83                 full_matrices: bool = False,
84                 compute_uv: bool = False,
85                 hermitian: bool = False) -> Tuple[np.ndarray, np.ndarray, Any]:
86        U, s = la.thin_svd(snapshots, self._comm, method='method_of_snapshots')
87        return U, s, 'not_computed_in_method_of_snapshots'

Parallel implementation of the method of snapshots to mimic the SVD for basis construction

Sample usage:

mySvd = SvdMethodOfSnapshots(comm) U,s,_ = mySvd(snapshots)

where snapshots is the local portion of a distributed memory array.

The standard reduced-basis problem requires solving the optimization problem $$ \boldsymbol \Phi = \underset{ \boldsymbol \Phi_{*} \in \mathbb{R}^{N \times K} | \boldsymbol \Phi_{*}^T \boldsymbol \Phi_{*} = \mathbf{I}}{ \mathrm{arg \; min} } \| \Phi_{*} \Phi_{*}^T \mathbf{S} - \mathbf{S} \|_2,$$ where $\mathbf{S} \in \mathbb{R}^{N \times N_s}$, with $N_s$ being the number of snapshots. The standard way to solve this is with the thin SVD. An alternative approach is to use the method of snapshts/kernel trick, see, e.g., https://web.stanford.edu/group/frg/course_work/CME345/CA-CME345-Ch4.pdf. Here, we instead solve the eigenvalue probelm $$ \mathbf{S}^T \mathbf{S} \boldsymbol \psi_i = \lambda_i \boldsymbol \psi_i$$ for $i = 1,\ldots,N_s$. It can be shown that the left singular vectors from the SVD of $\mathbf{S}$ are related to the eigen-vectors of the above by $$ \mathbf{u}_i = \frac{1}{\sqrt{\lambda_i}} \mathbf{S} \boldsymbol \psi_i.$$

An advantage of the method of snapshots is that it can be easily parallelized and is efficient if we don't have many snapshots. We compute $\mathbf{S}^T \mathbf{S}$ in parallel, and then solve the (typically small) eigenvalue problem in serial.

SvdMethodOfSnapshots(comm)
79    def __init__(self, comm) -> None:
80        self._comm = comm
class SvdMethodOfSnapshotsForQr:
 90class SvdMethodOfSnapshotsForQr:
 91    '''
 92    Same as SvdMethodOfSnapshots, but call only returns two arguments to be
 93    compatible with QR routine.
 94    '''
 95    def __init__(self, comm) -> None:
 96        self._comm = comm
 97
 98    def __call__(self, snapshots: np.ndarray,
 99                 full_matrices: bool = False,
100                 compute_uv: bool = False,
101                 hermitian: bool = False) -> Tuple[np.ndarray, Any]:
102        U, _ = la.thin_svd(snapshots, self._comm, method='method_of_snapshots')
103        return U, 'not_computed_in_method_of_snapshots'

Same as SvdMethodOfSnapshots, but call only returns two arguments to be compatible with QR routine.

SvdMethodOfSnapshotsForQr(comm)
95    def __init__(self, comm) -> None:
96        self._comm = comm