Example #1
0
def extend_basis(U, basis, product=None, method='gram_schmidt', pod_modes=1, pod_orthonormalize=True, copy_U=True):
    assert method in ('trivial', 'gram_schmidt', 'pod')

    basis_length = len(basis)

    if method == 'trivial':
        remove = set()
        for i in range(len(U)):
            if np.any(almost_equal(U[i], basis)):
                remove.add(i)
        basis.append(U[[i for i in range(len(U)) if i not in remove]],
                     remove_from_other=(not copy_U))
    elif method == 'gram_schmidt':
        basis.append(U, remove_from_other=(not copy_U))
        gram_schmidt(basis, offset=basis_length, product=product, copy=False, check=False)
    elif method == 'pod':
        U_proj_err = U - basis.lincomb(U.inner(basis, product))

        basis.append(pod(U_proj_err, modes=pod_modes, product=product, orth_tol=np.inf)[0])

        if pod_orthonormalize:
            gram_schmidt(basis, offset=basis_length, product=product, copy=False, check=False)

    if len(basis) <= basis_length:
        raise ExtensionError
Example #2
0
        def build_basis(self):
            """Compute a reduced basis using proper orthogonal decomposition."""
            self.training_data = []

            with self.logger.block('Building reduced basis ...'):

                # compute snapshots for POD and training of neural networks
                with self.logger.block('Computing training snapshots ...'):
                    U = self.fom.solution_space.empty()
                    for mu in self.training_set:
                        U.append(self.fom.solve(mu))

                # compute reduced basis via POD
                reduced_basis, svals = pod(U,
                                           modes=self.basis_size,
                                           rtol=self.rtol / 2.,
                                           atol=self.atol / 2.,
                                           l2_err=self.l2_err / 2.,
                                           **(self.pod_params or {}))

                # determine the coefficients of the full-order solutions in the reduced basis to obtain the
                # training data; convert everything into tensors that are compatible with PyTorch
                for mu, u in zip(self.training_set, U):
                    mu_tensor = torch.DoubleTensor(mu.to_numpy())
                    u_tensor = torch.DoubleTensor(reduced_basis.inner(u)[:, 0])
                    self.training_data.append((mu_tensor, u_tensor))

            # compute mean square loss
            mean_square_loss = (sum(U.norm2()) - sum(svals**2)) / len(U)

            return reduced_basis, mean_square_loss
Example #3
0
        def build_basis(self):
            """Compute a reduced basis using proper orthogonal decomposition."""
            with self.logger.block('Building reduced basis ...'):

                # compute snapshots for POD and training of neural networks
                with self.logger.block('Computing training snapshots ...'):
                    U = self.fom.solution_space.empty()
                    for mu in self.training_set:
                        u = self.fom.solve(mu)
                        if hasattr(self, 'nt'):
                            assert self.nt == len(u)
                        else:
                            self.nt = len(u)
                        U.append(u)

                # compute reduced basis via POD
                reduced_basis, svals = pod(U,
                                           modes=self.basis_size,
                                           rtol=self.rtol / 2.,
                                           atol=self.atol / 2.,
                                           l2_err=self.l2_err / 2.,
                                           **(self.pod_params or {}))

                self.training_data = []
                for i, mu in enumerate(self.training_set):
                    sample = self._compute_sample(
                        mu, U[i * self.nt:(i + 1) * self.nt], reduced_basis)
                    self.training_data.extend(sample)

            # compute mean square loss
            mean_square_loss = (sum(U.norm2()) - sum(svals**2)) / len(U)

            return reduced_basis, mean_square_loss
Example #4
0
def reduce_pod(d, reductor, snapshots_per_block, basis_size):
    from pymor.algorithms.pod import pod

    tic = time.time()

    training_set = d.parameter_space.sample_uniformly(snapshots_per_block)

    print('Solving on training set ...')
    snapshots = d.operator.source.empty(reserve=len(training_set))
    for mu in training_set:
        snapshots.append(d.solve(mu))

    print('Performing POD ...')
    basis, singular_values = pod(snapshots, modes=basis_size, product=reductor.product)

    print('Reducing ...')
    reductor.extend_basis(basis, 'trivial')
    rd = reductor.reduce()

    elapsed_time = time.time() - tic

    # generate summary
    real_rb_size = rd.solution_space.dim
    training_set_size = len(training_set)
    summary = '''POD basis generation:
   size of training set:   {training_set_size}
   prescribed basis size:  {basis_size}
   actual basis size:      {real_rb_size}
   elapsed time:           {elapsed_time}
'''.format(**locals())

    return rd, summary
Example #5
0
File: basic.py Project: pymor/pymor
def extend_basis(U, basis, product=None, method='gram_schmidt', pod_modes=1, pod_orthonormalize=True, copy_U=True):
    assert method in ('trivial', 'gram_schmidt', 'pod')

    basis_length = len(basis)

    if method == 'trivial':
        remove = set()
        for i in range(len(U)):
            if np.any(almost_equal(U[i], basis)):
                remove.add(i)
        basis.append(U[[i for i in range(len(U)) if i not in remove]],
                     remove_from_other=(not copy_U))
    elif method == 'gram_schmidt':
        basis.append(U, remove_from_other=(not copy_U))
        gram_schmidt(basis, offset=basis_length, product=product, copy=False, check=False)
    elif method == 'pod':
        U_proj_err = U - basis.lincomb(U.inner(basis, product))

        basis.append(pod(U_proj_err, modes=pod_modes, product=product, orthonormalize=False)[0])

        if pod_orthonormalize:
            gram_schmidt(basis, offset=basis_length, product=product, copy=False, check=False)

    if len(basis) <= basis_length:
        raise ExtensionError
Example #6
0
def reduce_pod(fom, reductor, snapshots_per_block, basis_size):
    from pymor.algorithms.pod import pod

    tic = time.time()

    training_set = fom.parameter_space.sample_uniformly(snapshots_per_block)

    print('Solving on training set ...')
    snapshots = fom.operator.source.empty(reserve=len(training_set))
    for mu in training_set:
        snapshots.append(fom.solve(mu))

    print('Performing POD ...')
    basis, singular_values = pod(snapshots, modes=basis_size, product=reductor.products['RB'])

    print('Reducing ...')
    reductor.extend_basis(basis, method='trivial')
    rom = reductor.reduce()

    elapsed_time = time.time() - tic

    # generate summary
    real_rb_size = rom.solution_space.dim
    training_set_size = len(training_set)
    summary = f'''POD basis generation:
   size of training set:   {training_set_size}
   prescribed basis size:  {basis_size}
   actual basis size:      {real_rb_size}
   elapsed time:           {elapsed_time}
'''

    return rom, summary
Example #7
0
def default_pod_method(U, eps, is_root_node, product):
    return pod(U,
               atol=0.,
               rtol=0.,
               l2_err=eps,
               product=product,
               orth_tol=None if is_root_node else np.inf)
Example #8
0
def default_pod_method(U, eps, is_root_node, product):
    return pod(U,
               atol=0.,
               rtol=0.,
               l2_err=eps,
               product=product,
               orthonormalize=is_root_node,
               check=False)
Example #9
0
File: pod.py Project: shmlzr/pymor
def test_pod_with_product(operator_with_arrays_and_products, method):
    _, _, A, _, p, _ = operator_with_arrays_and_products

    B = A.copy()
    with log_levels({"pymor.algorithms": "ERROR"}):
        U, s = pod(A, product=p, method=method)
    assert np.all(almost_equal(A, B))
    assert len(U) == len(s)
    assert np.allclose(U.gramian(p), np.eye(len(s)))
Example #10
0
def test_pod_with_product(operator_with_arrays_and_products, method):
    _, _, A, _, p, _ = operator_with_arrays_and_products
    print(type(A))
    print(A.dim, len(A))

    B = A.copy()
    U, s = pod(A, product=p, method=method)
    assert np.all(almost_equal(A, B))
    assert len(U) == len(s)
    assert np.allclose(U.gramian(p), np.eye(len(s)))
Example #11
0
def test_pod(vector_array, method):
    A = vector_array
    print(type(A))
    print(A.dim, len(A))

    B = A.copy()
    U, s = pod(A, method=method)
    assert np.all(almost_equal(A, B))
    assert len(U) == len(s)
    assert np.allclose(U.gramian(), np.eye(len(s)))
Example #12
0
File: pod.py Project: shmlzr/pymor
def test_pod(vector_array, method):
    A = vector_array
    # TODO assumption here masks a potential issue with the algorithm
    #      where it fails in internal lapack instead of a proper error
    assume(len(A) > 1 or A.dim > 1)
    assume(not contains_zero_vector(A, rtol=1e-13, atol=1e-13))

    B = A.copy()
    orth_tol = 1e-10
    U, s = pod(A, method=method, orth_tol=orth_tol)
    assert np.all(almost_equal(A, B))
    assert len(U) == len(s)
    assert np.allclose(U.gramian(), np.eye(len(s)), atol=orth_tol)
Example #13
0
def hapod_demo(args):
    args['--grid'] = int(args['--grid'])
    args['--nt'] = int(args['--nt'])
    args['--omega'] = float(args['--omega'])
    args['--procs'] = int(args['--procs'])
    args['--snap'] = int(args['--snap'])
    args['--threads'] = int(args['--threads'])
    args['TOL'] = float(args['TOL'])
    args['DIST'] = int(args['DIST'])
    args['INC'] = int(args['INC'])
    assert args['--procs'] == 0 or args['--threads'] == 0

    tol = args['TOL']
    omega = args['--omega']
    executor = ProcessPoolExecutor(args['--procs']) if args['--procs'] > 0 else \
        ThreadPoolExecutor(args['--threads']) if args['--threads'] > 0 else \
        None

    p = burgers_problem_2d()
    d, data = discretize_instationary_fv(p, grid_type=RectGrid, diameter=np.sqrt(2)/args['--grid'], nt=args['--nt'])

    U = d.solution_space.empty()
    for mu in d.parameter_space.sample_randomly(args['--snap']):
        U.append(d.solve(mu))

    tic = time()
    pod_modes = pod(U, l2_err=tol * np.sqrt(len(U)), product=d.l2_product, check=False)[0]
    pod_time = time() - tic

    tic = time()
    dist_modes = dist_vectorarray_hapod(args['DIST'], U, tol, omega, product=d.l2_product, executor=executor)[0]
    dist_time = time() - tic

    tic = time()
    inc_modes = inc_vectorarray_hapod(args['INC'], U, tol, omega, product=d.l2_product)[0]
    inc_time = time() - tic

    print('Snapshot matrix: {} x {}'.format(U.dim, len(U)))
    print(format_table([
        ['Method', 'Error', 'Modes', 'Time'],
        ['POD', np.linalg.norm(d.l2_norm(U-pod_modes.lincomb(d.l2_product.apply2(U, pod_modes)))/np.sqrt(len(U))),
         len(pod_modes), pod_time],
        ['DIST HAPOD', np.linalg.norm(d.l2_norm(U-dist_modes.lincomb(d.l2_product.apply2(U, dist_modes)))/np.sqrt(len(U))),
         len(dist_modes), dist_time],
        ['INC HAPOD', np.linalg.norm(d.l2_norm(U-inc_modes.lincomb(d.l2_product.apply2(U, inc_modes)))/np.sqrt(len(U))),
         len(inc_modes), inc_time]]
    ))
Example #14
0
def reduce_pod(d, reductor, snapshots_per_block, product_name, basis_size):
    from pymor.algorithms.pod import pod

    tic = time.time()

    training_set = d.parameter_space.sample_uniformly(snapshots_per_block)

    print("Solving on training set ...")
    snapshots = d.operator.source.empty(reserve=len(training_set))
    for mu in training_set:
        snapshots.append(d.solve(mu))

    if product_name == "h1":
        pod_product = d.h1_0_semi_product
    elif product_name == "euclidean":
        pod_product = None
    else:
        assert False

    print("Performing POD ...")
    basis, singular_values = pod(snapshots, modes=basis_size, product=pod_product)

    print("Reducing ...")
    rd, rc, _ = reductor(d, basis)

    elapsed_time = time.time() - tic

    # generate summary
    real_rb_size = rd.solution_space.dim
    training_set_size = len(training_set)
    summary = """POD basis generation:
   size of training set:   {training_set_size}
   inner product for POD:  {product_name}
   prescribed basis size:  {basis_size}
   actual basis size:      {real_rb_size}
   elapsed time:           {elapsed_time}
""".format(
        **locals()
    )

    return rd, rc, summary
Example #15
0
def reduce_pod(d, reductor, snapshots_per_block, product_name, basis_size):
    from pymor.algorithms.pod import pod

    tic = time.time()

    training_set = d.parameter_space.sample_uniformly(snapshots_per_block)

    print('Solving on training set ...')
    snapshots = d.operator.source.empty(reserve=len(training_set))
    for mu in training_set:
        snapshots.append(d.solve(mu))

    if product_name == 'h1':
        pod_product = d.h1_0_semi_product
    elif product_name == 'euclidean':
        pod_product = None
    else:
        assert False

    print('Performing POD ...')
    basis, singular_values = pod(snapshots,
                                 modes=basis_size,
                                 product=pod_product)

    print('Reducing ...')
    rd, rc, _ = reductor(d, basis)

    elapsed_time = time.time() - tic

    # generate summary
    real_rb_size = rd.solution_space.dim
    training_set_size = len(training_set)
    summary = '''POD basis generation:
   size of training set:   {training_set_size}
   inner product for POD:  {product_name}
   prescribed basis size:  {basis_size}
   actual basis size:      {real_rb_size}
   elapsed time:           {elapsed_time}
'''.format(**locals())

    return rd, rc, summary
Example #16
0
def thermalblock_demo(args):
    args['XBLOCKS'] = int(args['XBLOCKS'])
    args['YBLOCKS'] = int(args['YBLOCKS'])
    args['--grid'] = int(args['--grid'])
    args['SNAPSHOTS'] = int(args['SNAPSHOTS'])
    args['RBSIZE'] = int(args['RBSIZE'])
    args['--test'] = int(args['--test'])
    args['--pod-norm'] = args['--pod-norm'].lower()
    assert args['--pod-norm'] in {'trivial', 'h1'}

    print('Solving on TriaGrid(({0},{0}))'.format(args['--grid']))

    print('Setup Problem ...')
    problem = ThermalBlockProblem(num_blocks=(args['XBLOCKS'], args['YBLOCKS']))

    print('Discretize ...')
    discretization, _ = discretize_elliptic_cg(problem, diameter=1. / args['--grid'])

    print('The parameter type is {}'.format(discretization.parameter_type))

    if args['--plot-solutions']:
        print('Showing some solutions')
        Us = tuple()
        legend = tuple()
        for mu in discretization.parameter_space.sample_randomly(2):
            print('Solving for diffusion = \n{} ... '.format(mu['diffusion']))
            sys.stdout.flush()
            Us = Us + (discretization.solve(mu),)
            legend = legend + (str(mu['diffusion']),)
        discretization.visualize(Us, legend=legend, title='Detailed Solutions for different parameters', block=True)

    print('RB generation ...')

    tic = time.time()

    print('Solving on training set ...')
    S_train = list(discretization.parameter_space.sample_uniformly(args['SNAPSHOTS']))
    snapshots = discretization.operator.source.empty(reserve=len(S_train))
    for mu in S_train:
        snapshots.append(discretization.solve(mu))

    print('Performing POD ...')
    pod_product = discretization.h1_product if args['--pod-norm'] == 'h1' else None
    rb = pod(snapshots, modes=args['RBSIZE'], product=pod_product)[0]

    print('Reducing ...')
    reductor = reduce_generic_rb
    rb_discretization, reconstructor, _ = reductor(discretization, rb)

    toc = time.time()
    t_offline = toc - tic

    print('\nSearching for maximum error on random snapshots ...')

    tic = time.time()
    h1_err_max = -1
    cond_max = -1
    for mu in discretization.parameter_space.sample_randomly(args['--test']):
        print('Solving RB-Scheme for mu = {} ... '.format(mu), end='')
        URB = reconstructor.reconstruct(rb_discretization.solve(mu))
        U = discretization.solve(mu)
        h1_err = discretization.h1_norm(U - URB)[0]
        cond = np.linalg.cond(rb_discretization.operator.assemble(mu)._matrix)
        if h1_err > h1_err_max:
            h1_err_max = h1_err
            mumax = mu
        if cond > cond_max:
            cond_max = cond
            cond_max_mu = mu
        print('H1-error = {}, condition = {}'.format(h1_err, cond))
    toc = time.time()
    t_est = toc - tic
    real_rb_size = len(rb)

    print('''
    *** RESULTS ***

    Problem:
       number of blocks:                   {args[XBLOCKS]}x{args[YBLOCKS]}
       h:                                  sqrt(2)/{args[--grid]}

    POD basis generation:
       number of snapshots:                {args[SNAPSHOTS]}^({args[XBLOCKS]}x{args[YBLOCKS]})
       pod norm:                           {args[--pod-norm]}
       prescribed basis size:              {args[RBSIZE]}
       actual basis size:                  {real_rb_size}
       elapsed time:                       {t_offline}

    Stochastic error estimation:
       number of samples:                  {args[--test]}
       maximal H1-error:                   {h1_err_max}  (mu = {mumax})
       maximal condition of system matrix: {cond_max}  (mu = {cond_max_mu})
       elapsed time:                       {t_est}
    '''.format(**locals()))

    sys.stdout.flush()
    if args['--plot-err']:
        discretization.visualize((U, URB, U - URB), legend=('Detailed Solution', 'Reduced Solution', 'Error'),
                                 title='Maximum Error Solution', separate_colorbars=True, block=True)
Example #17
0
def reduce_pod(fom, reductor, snapshots, basis_size):
    basis, singular_values = pod(snapshots, modes=basis_size)
    reductor.extend_basis(basis, method='trivial')
    rom = reductor.reduce()

    return rom
Example #18
0
    fom = InstationaryModel(T=120.,
                            initial_data=q0_array,
                            operator=op,
                            rhs=None,
                            mass=None,
                            visualizer=vis,
                            time_stepper=ts,
                            num_values=20)

    # Constant Timesteps
    dt = fom.T / fom.time_stepper.nt
    problem.claw.solver.dt = dt

    U = fom.solve(mu=None)  # Solve
    modes, svals = pod(U)
    print(np.shape(modes.to_numpy()))
    print(svals)

    ## Write ROM to File
    # writeROMtoFile(U, modes, outDir)

    # Plot the singular values
    plt.figure()
    plt.semilogy(svals, linewidth=2, label='Singular Values')
    plt.legend()
    plt.show()

    fom.visualize(U)  # plot the solution

    num_output_times = fom.time_stepper.nt
Example #19
0
def hapod_demo(args):
    args['--grid'] = int(args['--grid'])
    args['--nt'] = int(args['--nt'])
    args['--omega'] = float(args['--omega'])
    args['--procs'] = int(args['--procs'])
    args['--snap'] = int(args['--snap'])
    args['--threads'] = int(args['--threads'])
    args['TOL'] = float(args['TOL'])
    args['DIST'] = int(args['DIST'])
    args['INC'] = int(args['INC'])
    assert args['--procs'] == 0 or args['--threads'] == 0

    tol = args['TOL']
    omega = args['--omega']
    executor = ProcessPoolExecutor(args['--procs']) if args['--procs'] > 0 else \
        ThreadPoolExecutor(args['--threads']) if args['--threads'] > 0 else \
        None

    p = burgers_problem_2d()
    m, data = discretize_instationary_fv(p,
                                         grid_type=RectGrid,
                                         diameter=np.sqrt(2) / args['--grid'],
                                         nt=args['--nt'])

    U = m.solution_space.empty()
    for mu in m.parameter_space.sample_randomly(args['--snap']):
        U.append(m.solve(mu))

    tic = time()
    pod_modes = pod(U,
                    l2_err=tol * np.sqrt(len(U)),
                    product=m.l2_product,
                    check=False)[0]
    pod_time = time() - tic

    tic = time()
    dist_modes = dist_vectorarray_hapod(args['DIST'],
                                        U,
                                        tol,
                                        omega,
                                        product=m.l2_product,
                                        executor=executor)[0]
    dist_time = time() - tic

    tic = time()
    inc_modes = inc_vectorarray_hapod(args['INC'],
                                      U,
                                      tol,
                                      omega,
                                      product=m.l2_product)[0]
    inc_time = time() - tic

    print(f'Snapshot matrix: {U.dim} x {len(U)}')
    print(
        format_table([
            ['Method', 'Error', 'Modes', 'Time'],
            [
                'POD',
                np.linalg.norm(
                    m.l2_norm(U - pod_modes.lincomb(
                        m.l2_product.apply2(U, pod_modes))) / np.sqrt(len(U))),
                len(pod_modes), pod_time
            ],
            [
                'DIST HAPOD',
                np.linalg.norm(
                    m.l2_norm(U - dist_modes.lincomb(
                        m.l2_product.apply2(U, dist_modes))) /
                    np.sqrt(len(U))),
                len(dist_modes), dist_time
            ],
            [
                'INC HAPOD',
                np.linalg.norm(
                    m.l2_norm(U - inc_modes.lincomb(
                        m.l2_product.apply2(U, inc_modes))) / np.sqrt(len(U))),
                len(inc_modes), inc_time
            ]
        ]))
Example #20
0
File: hapod.py Project: pymor/pymor
def default_pod_method(U, eps, is_root_node, product):
    return pod(U, atol=0., rtol=0.,
               l2_err=eps, product=product,
               orthonormalize=is_root_node, check=False)
Example #21
0
                                   visualizer=visualizer,
                                   name='C++-Discretization',
                                   cache_region=None)
    return d


# discretize
d = discretize(50, 10000, 4)

# generate solution snapshots
snapshots = d.solution_space.empty()
for mu in d.parameter_space.sample_uniformly(2):
    snapshots.append(d.solve(mu))

# apply POD
reduced_basis = pod(snapshots, 4)[0]

# reduce the model
rd, rc, _ = reduce_generic_rb(d, reduced_basis)

# stochastic error estimation
mu_max = None
err_max = -1.
for mu in d.parameter_space.sample_randomly(10):
    U_RB = (rc.reconstruct(rd.solve(mu)))
    U = d.solve(mu)
    err = np.max((U_RB - U).l2_norm())
    if err > err_max:
        err_max = err
        mu_max = mu
Example #22
0
File: ei.py Project: fameyer/pymor
def deim(U, modes=None, error_norm=None, product=None):
    """Generate data for empirical interpolation using DEIM algorithm.

    Given a |VectorArray| `U`, this method generates a collateral basis and
    interpolation DOFs for empirical interpolation of the vectors contained in `U`.
    The returned objects can also be used to instantiate an |EmpiricalInterpolatedOperator|.

    The collateral basis is determined by the first :func:`~pymor.algorithms.pod.pod` modes of `U`.

    Parameters
    ----------
    U
        A |VectorArray| of vectors to interpolate.
    modes
        Dimension of the collateral basis i.e. number of POD modes of the vectors in `U`.
    error_norm
        Norm w.r.t. which to calculate the interpolation error. If `None`, the Euclidean norm
        is used.
    product
        Product |Operator| used for POD.

    Returns
    -------
    interpolation_dofs
        |NumPy array| of the DOFs at which the vectors are interpolated.
    collateral_basis
        |VectorArray| containing the generated collateral basis.
    data
        Dict containing the following fields:

            :errors: Sequence of maximum approximation errors during greedy search.
    """

    assert isinstance(U, VectorArrayInterface)

    logger = getLogger("pymor.algorithms.ei.deim")
    logger.info("Generating Interpolation Data ...")

    collateral_basis = pod(U, modes, product=product)[0]

    interpolation_dofs = np.zeros((0,), dtype=np.int32)
    interpolation_matrix = np.zeros((0, 0))
    errs = []

    for i in xrange(len(collateral_basis)):

        if len(interpolation_dofs) > 0:
            coefficients = np.linalg.solve(
                interpolation_matrix, collateral_basis.components(interpolation_dofs, ind=i).T
            ).T
            U_interpolated = collateral_basis.lincomb(coefficients, ind=range(len(interpolation_dofs)))
            ERR = collateral_basis.copy(ind=i)
            ERR -= U_interpolated
        else:
            ERR = collateral_basis.copy(ind=i)

        err = ERR.l2_norm() if error_norm is None else error_norm(ERR)

        logger.info("Interpolation error for basis vector {}: {}".format(i, err))

        # compute new interpolation dof and collateral basis vector
        new_dof = ERR.amax()[0][0]

        if new_dof in interpolation_dofs:
            logger.info("DOF {} selected twice for interplation! Stopping extension loop.".format(new_dof))
            break

        interpolation_dofs = np.hstack((interpolation_dofs, new_dof))
        interpolation_matrix = collateral_basis.components(interpolation_dofs, ind=range(len(interpolation_dofs))).T
        errs.append(err)

        logger.info("")

    if len(interpolation_dofs) < len(collateral_basis):
        collateral_basis.remove(ind=range(len(interpolation_dofs), len(collateral_basis)))

    logger.info("Finished.".format(new_dof))

    data = {"errors": errs}

    return interpolation_dofs, collateral_basis, data
def fenics_nonlinear_demo(args):
    DIM = int(args['DIM'])
    N = int(args['N'])
    ORDER = int(args['ORDER'])

    from pymor.tools import mpi

    if mpi.parallel:
        from pymor.models.mpi import mpi_wrap_model
        local_models = mpi.call(mpi.function_call_manage, discretize, DIM, N,
                                ORDER)
        fom = mpi_wrap_model(local_models,
                             use_with=True,
                             pickle_local_spaces=False)
    else:
        fom = discretize(DIM, N, ORDER)

    # ### ROM generation (POD/DEIM)
    from pymor.algorithms.ei import ei_greedy
    from pymor.algorithms.newton import newton
    from pymor.algorithms.pod import pod
    from pymor.operators.ei import EmpiricalInterpolatedOperator
    from pymor.reductors.basic import StationaryRBReductor

    U = fom.solution_space.empty()
    residuals = fom.solution_space.empty()
    for mu in fom.parameter_space.sample_uniformly(10):
        UU, data = newton(fom.operator,
                          fom.rhs.as_vector(),
                          mu=mu,
                          rtol=1e-6,
                          return_residuals=True)
        U.append(UU)
        residuals.append(data['residuals'])

    dofs, cb, _ = ei_greedy(residuals, rtol=1e-7)
    ei_op = EmpiricalInterpolatedOperator(fom.operator,
                                          collateral_basis=cb,
                                          interpolation_dofs=dofs,
                                          triangular=True)

    rb, svals = pod(U, rtol=1e-7)
    fom_ei = fom.with_(operator=ei_op)
    reductor = StationaryRBReductor(fom_ei, rb)
    rom = reductor.reduce()
    # the reductor currently removes all solver_options so we need to add them again
    rom = rom.with_(operator=rom.operator.with_(
        solver_options=fom.operator.solver_options))

    # ### ROM validation
    import time
    import numpy as np

    # ensure that FFC is not called during runtime measurements
    rom.solve(1)

    errs = []
    speedups = []
    for mu in fom.parameter_space.sample_randomly(10):
        tic = time.time()
        U = fom.solve(mu)
        t_fom = time.time() - tic

        tic = time.time()
        u_red = rom.solve(mu)
        t_rom = time.time() - tic

        U_red = reductor.reconstruct(u_red)
        errs.append(((U - U_red).l2_norm() / U.l2_norm())[0])
        speedups.append(t_fom / t_rom)
    print(f'Maximum relative ROM error: {max(errs)}')
    print(f'Median of ROM speedup: {np.median(speedups)}')
Example #24
0
def deim(U, modes=None, error_norm=None, product=None):
    """Generate data for empirical interpolation using DEIM algorithm.

    Given a |VectorArray| `U`, this method generates a collateral basis and
    interpolation DOFs for empirical interpolation of the vectors contained in `U`.
    The returned objects can be used to instantiate an |EmpiricalInterpolatedOperator|
    (with `triangular=False`).

    The collateral basis is determined by the first :func:`~pymor.algorithms.pod.pod` modes of `U`.

    Parameters
    ----------
    U
        A |VectorArray| of vectors to interpolate.
    modes
        Dimension of the collateral basis i.e. number of POD modes of the vectors in `U`.
    error_norm
        Norm w.r.t. which to calculate the interpolation error. If `None`, the Euclidean norm
        is used.
    product
        Inner product |Operator| used for the POD.

    Returns
    -------
    interpolation_dofs
        |NumPy array| of the DOFs at which the vectors are interpolated.
    collateral_basis
        |VectorArray| containing the generated collateral basis.
    data
        Dict containing the following fields:

            :errors: Sequence of maximum approximation errors during greedy search.
    """

    assert isinstance(U, VectorArrayInterface)

    logger = getLogger('pymor.algorithms.ei.deim')
    logger.info('Generating Interpolation Data ...')

    collateral_basis = pod(U, modes, product=product)[0]

    interpolation_dofs = np.zeros((0,), dtype=np.int32)
    interpolation_matrix = np.zeros((0, 0))
    errs = []

    for i in range(len(collateral_basis)):

        if len(interpolation_dofs) > 0:
            coefficients = np.linalg.solve(interpolation_matrix,
                                           collateral_basis[i].dofs(interpolation_dofs).T).T
            U_interpolated = collateral_basis[:len(interpolation_dofs)].lincomb(coefficients)
            ERR = collateral_basis[i].copy()
            ERR -= U_interpolated
        else:
            ERR = collateral_basis[i].copy()

        err = np.max(ERR.l2_norm() if error_norm is None else error_norm(ERR))

        logger.info('Interpolation error for basis vector {}: {}'.format(i, err))

        # compute new interpolation dof and collateral basis vector
        new_dof = ERR.amax()[0][0]

        if new_dof in interpolation_dofs:
            logger.info('DOF {} selected twice for interplation! Stopping extension loop.'.format(new_dof))
            break

        interpolation_dofs = np.hstack((interpolation_dofs, new_dof))
        interpolation_matrix = collateral_basis[:len(interpolation_dofs)].dofs(interpolation_dofs).T
        errs.append(err)

        logger.info('')

    if len(interpolation_dofs) < len(collateral_basis):
        del collateral_basis[len(interpolation_dofs):len(collateral_basis)]

    logger.info('Finished.')

    data = {'errors': errs}

    return interpolation_dofs, collateral_basis, data
Example #25
0
def main(
        dim: int = Argument(..., help='Spatial dimension of the problem.'),
        n: int = Argument(
            ..., help='Number of mesh intervals per spatial dimension.'),
        order: int = Argument(..., help='Finite element order.'),
):
    """Reduces a FEniCS-based nonlinear diffusion problem using POD/DEIM."""

    from pymor.tools import mpi

    if mpi.parallel:
        from pymor.models.mpi import mpi_wrap_model
        local_models = mpi.call(mpi.function_call_manage, discretize, dim, n,
                                order)
        fom = mpi_wrap_model(local_models,
                             use_with=True,
                             pickle_local_spaces=False)
    else:
        fom = discretize(dim, n, order)

    parameter_space = fom.parameters.space((0, 1000.))

    # ### ROM generation (POD/DEIM)
    from pymor.algorithms.ei import ei_greedy
    from pymor.algorithms.newton import newton
    from pymor.algorithms.pod import pod
    from pymor.operators.ei import EmpiricalInterpolatedOperator
    from pymor.reductors.basic import StationaryRBReductor

    U = fom.solution_space.empty()
    residuals = fom.solution_space.empty()
    for mu in parameter_space.sample_uniformly(10):
        UU, data = newton(fom.operator,
                          fom.rhs.as_vector(),
                          mu=mu,
                          rtol=1e-6,
                          return_residuals=True)
        U.append(UU)
        residuals.append(data['residuals'])

    dofs, cb, _ = ei_greedy(residuals, rtol=1e-7)
    ei_op = EmpiricalInterpolatedOperator(fom.operator,
                                          collateral_basis=cb,
                                          interpolation_dofs=dofs,
                                          triangular=True)

    rb, svals = pod(U, rtol=1e-7)
    fom_ei = fom.with_(operator=ei_op)
    reductor = StationaryRBReductor(fom_ei, rb)
    rom = reductor.reduce()
    # the reductor currently removes all solver_options so we need to add them again
    rom = rom.with_(operator=rom.operator.with_(
        solver_options=fom.operator.solver_options))

    # ### ROM validation
    import time
    import numpy as np

    # ensure that FFC is not called during runtime measurements
    rom.solve(1)

    errs = []
    speedups = []
    for mu in parameter_space.sample_randomly(10):
        tic = time.perf_counter()
        U = fom.solve(mu)
        t_fom = time.perf_counter() - tic

        tic = time.perf_counter()
        u_red = rom.solve(mu)
        t_rom = time.perf_counter() - tic

        U_red = reductor.reconstruct(u_red)
        errs.append(((U - U_red).norm() / U.norm())[0])
        speedups.append(t_fom / t_rom)
    print(f'Maximum relative ROM error: {max(errs)}')
    print(f'Median of ROM speedup: {np.median(speedups)}')
Example #26
0
                            time_stepper=time_stepper, num_values=20,
                            visualizer=visualizer, name='C++-Model')
    return fom


# discretize
fom = discretize(50, 10000, 4)
parameter_space = fom.parameters.space(0.1, 1)

# generate solution snapshots
snapshots = fom.solution_space.empty()
for mu in parameter_space.sample_uniformly(2):
    snapshots.append(fom.solve(mu))

# apply POD
reduced_basis = pod(snapshots, modes=4)[0]

# reduce the model
reductor = InstationaryRBReductor(fom, reduced_basis, check_orthonormality=True)
rom = reductor.reduce()

# stochastic error estimation
mu_max = None
err_max = -1.
for mu in parameter_space.sample_randomly(10):
    U_RB = (reductor.reconstruct(rom.solve(mu)))
    U = fom.solve(mu)
    err = np.max((U_RB-U).norm())
    if err > err_max:
        err_max = err
        mu_max = mu
Example #27
0
    def extend_basis(self, U, method='gram_schmidt', pod_modes=1, pod_orthonormalize=True, copy_U=True):
        """Extend basis by new vectors.

        Parameters
        ----------
        U
            |VectorArray| containing the new basis vectors.
        method
            Basis extension method to use. The following methods are available:

                :trivial:      Vectors in `U` are appended to the basis. Duplicate vectors
                               in the sense of :func:`~pymor.algorithms.basic.almost_equal`
                               are removed.
                :gram_schmidt: New basis vectors are orthonormalized w.r.t. to the old
                               basis using the :func:`~pymor.algorithms.gram_schmidt.gram_schmidt`
                               algorithm.
                :pod:          Append the first POD modes of the defects of the projections
                               of the vectors in U onto the existing basis
                               (e.g. for use in POD-Greedy algorithm).

            .. warning::
                In case of the `'gram_schmidt'` and `'pod'` extension methods, the existing reduced
                basis is assumed to be orthonormal w.r.t. the given inner product.

        pod_modes
            In case `method == 'pod'`, the number of POD modes that shall be appended to
            the basis.
        pod_orthonormalize
            If `True` and `method == 'pod'`, re-orthonormalize the new basis vectors obtained
            by the POD in order to improve numerical accuracy.
        copy_U
            If `copy_U` is `False`, the new basis vectors might be removed from `U`.

        Raises
        ------
        ExtensionError
            Raised when the selected extension method does not yield a basis of increased
            dimension.
        """
        assert method in ('trivial', 'gram_schmidt', 'pod')

        basis_length = len(self.RB)

        if method == 'trivial':
            remove = set()
            for i in range(len(U)):
                if np.any(almost_equal(U[i], self.RB)):
                    remove.add(i)
            self.RB.append(U[[i for i in range(len(U)) if i not in remove]],
                           remove_from_other=(not copy_U))
        elif method == 'gram_schmidt':
            self.RB.append(U, remove_from_other=(not copy_U))
            gram_schmidt(self.RB, offset=basis_length, product=self.product, copy=False)
        elif method == 'pod':
            if self.product is None:
                U_proj_err = U - self.RB.lincomb(U.dot(self.RB))
            else:
                U_proj_err = U - self.RB.lincomb(self.product.apply2(U, self.RB))

            self.RB.append(pod(U_proj_err, modes=pod_modes, product=self.product, orthonormalize=False)[0])

            if pod_orthonormalize:
                gram_schmidt(self.RB, offset=basis_length, product=self.product, copy=False)

        if len(self.RB) <= basis_length:
            raise ExtensionError
Example #28
0
def deim(U, modes=None, atol=None, rtol=None, product=None, pod_options={}):
    """Generate data for empirical interpolation using DEIM algorithm.

    Given a |VectorArray| `U`, this method generates a collateral basis and
    interpolation DOFs for empirical interpolation of the vectors contained in `U`.
    The returned objects can be used to instantiate an |EmpiricalInterpolatedOperator|
    (with `triangular=False`).

    The collateral basis is determined by the first :func:`~pymor.algorithms.pod.pod` modes of `U`.

    Parameters
    ----------
    U
        A |VectorArray| of vectors to interpolate.
    modes
        Dimension of the collateral basis i.e. number of POD modes of the vectors in `U`.
    atol
        Absolute POD tolerance.
    rtol
        Relative POD tolerance.
    product
        Inner product |Operator| used for the POD.
    pod_options
        Dictionary of additional options to pass to the :func:`~pymor.algorithms.pod.pod` algorithm.

    Returns
    -------
    interpolation_dofs
        |NumPy array| of the DOFs at which the vectors are interpolated.
    collateral_basis
        |VectorArray| containing the generated collateral basis.
    data
        Dict containing the following fields:

            :svals: POD singular values.
    """

    assert isinstance(U, VectorArrayInterface)

    logger = getLogger('pymor.algorithms.ei.deim')
    logger.info('Generating Interpolation Data ...')

    collateral_basis, svals = pod(U,
                                  modes=modes,
                                  atol=atol,
                                  rtol=rtol,
                                  product=product,
                                  **pod_options)

    interpolation_dofs = np.zeros((0, ), dtype=np.int32)
    interpolation_matrix = np.zeros((0, 0))

    for i in range(len(collateral_basis)):
        logger.info(f'Choosing interpolation point for basis vector {i}.')

        if len(interpolation_dofs) > 0:
            coefficients = solve(
                interpolation_matrix,
                collateral_basis[i].dofs(interpolation_dofs).T).T
            U_interpolated = collateral_basis[:len(interpolation_dofs
                                                   )].lincomb(coefficients)
            ERR = collateral_basis[i].copy()
            ERR -= U_interpolated
        else:
            ERR = collateral_basis[i].copy()

        # compute new interpolation dof and collateral basis vector
        new_dof = ERR.amax()[0][0]

        if new_dof in interpolation_dofs:
            logger.info(
                f'DOF {new_dof} selected twice for interplation! Stopping extension loop.'
            )
            break

        interpolation_dofs = np.hstack((interpolation_dofs, new_dof))
        interpolation_matrix = collateral_basis[:len(interpolation_dofs)].dofs(
            interpolation_dofs).T

    if len(interpolation_dofs) < len(collateral_basis):
        del collateral_basis[len(interpolation_dofs):len(collateral_basis)]

    logger.info('Finished.')

    data = {'svals': svals}

    return interpolation_dofs, collateral_basis, data
Example #29
0
def pod_basis_extension(basis, U, count=1, copy_basis=True, product=None, orthonormalize=True):
    """Extend basis with the first `count` POD modes of the projection of `U` onto the
    orthogonal complement of the basis.

    Note that the provided basis is assumed to be orthonormal w.r.t. the provided
    scalar product!

    Parameters
    ----------
    basis
        |VectorArray| containing the basis to extend. The basis is expected to be
        orthonormal w.r.t. `product`.
    U
        |VectorArray| containing the vectors to which the POD is applied.
    count
        Number of POD modes that are to be appended to the basis.
    product
        The scalar product w.r.t. which to orthonormalize; if `None`, the Euclidean
        product is used.
    copy_basis
        If `copy_basis` is `False`, the old basis is extended in-place.
    orthonormalize
        If `True`, re-orthonormalize the new basis vectors obtained by the POD
        in order to improve numerical accuracy.

    Returns
    -------
    new_basis
        The extended basis.
    extension_data
        Dict containing the following fields:

            :hierarchic: `True` if `new_basis` contains `basis` as its first vectors.

    Raises
    ------
    ExtensionError
        POD produces no new vectors. This is the case when no vector in `U`
        is linearly independent from the basis.
    """
    if basis is None:
        return pod(U, modes=count, product=product)[0], {'hierarchic': True}

    basis_length = len(basis)

    new_basis = basis.copy() if copy_basis else basis

    if product is None:
        U_proj_err = U - basis.lincomb(U.dot(basis))
    else:
        U_proj_err = U - basis.lincomb(product.apply2(U, basis))

    new_basis.append(pod(U_proj_err, modes=count, product=product, orthonormalize=False)[0])

    if orthonormalize:
        gram_schmidt(new_basis, offset=len(basis), product=product, copy=False)

    if len(new_basis) <= basis_length:
        raise ExtensionError

    return new_basis, {'hierarchic': True}
Example #30
0
def deim(U, modes=None, atol=None, rtol=None, product=None, pod_options={}):
    """Generate data for empirical interpolation using DEIM algorithm.

    Given a |VectorArray| `U`, this method generates a collateral basis and
    interpolation DOFs for empirical interpolation of the vectors contained in `U`.
    The returned objects can be used to instantiate an |EmpiricalInterpolatedOperator|
    (with `triangular=False`).

    The collateral basis is determined by the first :func:`~pymor.algorithms.pod.pod` modes of `U`.

    Parameters
    ----------
    U
        A |VectorArray| of vectors to interpolate.
    modes
        Dimension of the collateral basis i.e. number of POD modes of the vectors in `U`.
    atol
        Absolute POD tolerance.
    rtol
        Relative POD tolerance.
    product
        Inner product |Operator| used for the POD.
    pod_options
        Dictionary of additional options to pass to the :func:`~pymor.algorithms.pod.pod` algorithm.

    Returns
    -------
    interpolation_dofs
        |NumPy array| of the DOFs at which the vectors are interpolated.
    collateral_basis
        |VectorArray| containing the generated collateral basis.
    data
        Dict containing the following fields:

            :svals: POD singular values.
    """

    assert isinstance(U, VectorArrayInterface)

    logger = getLogger('pymor.algorithms.ei.deim')
    logger.info('Generating Interpolation Data ...')

    collateral_basis, svals = pod(U, modes=modes, atol=atol, rtol=rtol, product=product, **pod_options)

    interpolation_dofs = np.zeros((0,), dtype=np.int32)
    interpolation_matrix = np.zeros((0, 0))

    for i in range(len(collateral_basis)):
        logger.info('Choosing interpolation point for basis vector {}.'.format(i))

        if len(interpolation_dofs) > 0:
            coefficients = np.linalg.solve(interpolation_matrix,
                                           collateral_basis[i].dofs(interpolation_dofs).T).T
            U_interpolated = collateral_basis[:len(interpolation_dofs)].lincomb(coefficients)
            ERR = collateral_basis[i].copy()
            ERR -= U_interpolated
        else:
            ERR = collateral_basis[i].copy()

        # compute new interpolation dof and collateral basis vector
        new_dof = ERR.amax()[0][0]

        if new_dof in interpolation_dofs:
            logger.info('DOF {} selected twice for interplation! Stopping extension loop.'.format(new_dof))
            break

        interpolation_dofs = np.hstack((interpolation_dofs, new_dof))
        interpolation_matrix = collateral_basis[:len(interpolation_dofs)].dofs(interpolation_dofs).T

    if len(interpolation_dofs) < len(collateral_basis):
        del collateral_basis[len(interpolation_dofs):len(collateral_basis)]

    logger.info('Finished.')

    data = {'svals': svals}

    return interpolation_dofs, collateral_basis, data
Example #31
0
    def extend_basis(self,
                     U,
                     method='gram_schmidt',
                     pod_modes=1,
                     pod_orthonormalize=True,
                     copy_U=True):
        """Extend basis by new vectors.

        Parameters
        ----------
        U
            |VectorArray| containing the new basis vectors.
        method
            Basis extension method to use. The following methods are available:

                :trivial:      Vectors in `U` are appended to the basis. Duplicate vectors
                               in the sense of :func:`~pymor.algorithms.basic.almost_equal`
                               are removed.
                :gram_schmidt: New basis vectors are orthonormalized w.r.t. to the old
                               basis using the :func:`~pymor.algorithms.gram_schmidt.gram_schmidt`
                               algorithm.
                :pod:          Append the first POD modes of the defects of the projections
                               of the vectors in U onto the existing basis
                               (e.g. for use in POD-Greedy algorithm).

            .. warning::
                In case of the `'gram_schmidt'` and `'pod'` extension methods, the existing reduced
                basis is assumed to be orthonormal w.r.t. the given inner product.

        pod_modes
            In case `method == 'pod'`, the number of POD modes that shall be appended to
            the basis.
        pod_orthonormalize
            If `True` and `method == 'pod'`, re-orthonormalize the new basis vectors obtained
            by the POD in order to improve numerical accuracy.
        copy_U
            If `copy_U` is `False`, the new basis vectors might be removed from `U`.

        Raises
        ------
        ExtensionError
            Raised when the selected extension method does not yield a basis of increased
            dimension.
        """
        assert method in ('trivial', 'gram_schmidt', 'pod')

        basis_length = len(self.RB)

        if method == 'trivial':
            remove = set()
            for i in range(len(U)):
                if np.any(almost_equal(U[i], self.RB)):
                    remove.add(i)
            self.RB.append(U[[i for i in range(len(U)) if i not in remove]],
                           remove_from_other=(not copy_U))
        elif method == 'gram_schmidt':
            self.RB.append(U, remove_from_other=(not copy_U))
            gram_schmidt(self.RB,
                         offset=basis_length,
                         product=self.product,
                         copy=False)
        elif method == 'pod':
            if self.product is None:
                U_proj_err = U - self.RB.lincomb(U.dot(self.RB))
            else:
                U_proj_err = U - self.RB.lincomb(
                    self.product.apply2(U, self.RB))

            self.RB.append(
                pod(U_proj_err,
                    modes=pod_modes,
                    product=self.product,
                    orthonormalize=False)[0])

            if pod_orthonormalize:
                gram_schmidt(self.RB,
                             offset=basis_length,
                             product=self.product,
                             copy=False)

        if len(self.RB) <= basis_length:
            raise ExtensionError
Example #32
0
    d = InstationaryDiscretization(T=1e-0, operator=operator, rhs=rhs, initial_data=initial_data,
                                   time_stepper=time_stepper, num_values=20, parameter_space=parameter_space,
                                   visualizer=visualizer, name='C++-Discretization', cache_region=None)
    return d


# discretize
d = discretize(50, 10000, 4)

# generate solution snapshots
snapshots = d.solution_space.empty()
for mu in d.parameter_space.sample_uniformly(2):
    snapshots.append(d.solve(mu))

# apply POD
reduced_basis = pod(snapshots, 4)[0]

# reduce the model
rd, rc, _ = reduce_generic_rb(d, reduced_basis)

# stochastic error estimation
mu_max = None
err_max = -1.
for mu in d.parameter_space.sample_randomly(10):
    U_RB = (rc.reconstruct(rd.solve(mu)))
    U = d.solve(mu)
    err = np.max((U_RB-U).l2_norm())
    if err > err_max:
        err_max = err
        mu_max = mu
Example #33
0
def main(
    tol: float = Argument(..., help='Prescribed mean l2 approximation error.'),
    dist: int = Argument(..., help='Number of slices for distributed HAPOD.'),
    inc: int = Argument(..., help='Number of steps for incremental HAPOD.'),
    grid: int = Option(60, help='Use grid with (2*NI)*NI elements.'),
    nt: int = Option(100, help='Number of time steps.'),
    omega: float = Option(0.9, help='Parameter omega from HAPOD algorithm.'),
    procs: int = Option(
        0, help='Number of processes to use for parallelization.'),
    snap: int = Option(20, help='Number of snapshot trajectories to compute.'),
    threads: int = Option(
        0, help='Number of threads to use for parallelization.'),
):
    """Compression of snapshot data with the HAPOD algorithm from [HLR18]."""

    assert procs == 0 or threads == 0

    executor = ProcessPoolExecutor(procs) if procs > 0 else \
        ThreadPoolExecutor(threads) if threads > 0 else \
        None

    p = burgers_problem_2d()
    m, data = discretize_instationary_fv(p,
                                         grid_type=RectGrid,
                                         diameter=np.sqrt(2) / grid,
                                         nt=nt)

    U = m.solution_space.empty()
    for mu in p.parameter_space.sample_randomly(snap):
        U.append(m.solve(mu))

    tic = time.perf_counter()
    pod_modes = pod(U, l2_err=tol * np.sqrt(len(U)), product=m.l2_product)[0]
    pod_time = time.perf_counter() - tic

    tic = time.perf_counter()
    dist_modes = dist_vectorarray_hapod(dist,
                                        U,
                                        tol,
                                        omega,
                                        product=m.l2_product,
                                        executor=executor)[0]
    dist_time = time.perf_counter() - tic

    tic = time.perf_counter()
    inc_modes = inc_vectorarray_hapod(inc, U, tol, omega,
                                      product=m.l2_product)[0]
    inc_time = time.perf_counter() - tic

    print(f'Snapshot matrix: {U.dim} x {len(U)}')
    print(
        format_table([
            ['Method', 'Error', 'Modes', 'Time'],
            [
                'POD',
                np.linalg.norm(
                    m.l2_norm(U - pod_modes.lincomb(
                        m.l2_product.apply2(U, pod_modes))) / np.sqrt(len(U))),
                len(pod_modes), pod_time
            ],
            [
                'DIST HAPOD',
                np.linalg.norm(
                    m.l2_norm(U - dist_modes.lincomb(
                        m.l2_product.apply2(U, dist_modes))) /
                    np.sqrt(len(U))),
                len(dist_modes), dist_time
            ],
            [
                'INC HAPOD',
                np.linalg.norm(
                    m.l2_norm(U - inc_modes.lincomb(
                        m.l2_product.apply2(U, inc_modes))) / np.sqrt(len(U))),
                len(inc_modes), inc_time
            ]
        ]))