def reduce(self): if self.residual_range is not False: with self.logger.block('Estimating residual range ...'): try: self.residual_range, self.residual_range_dims = \ estimate_image_hierarchical([self.operator], [self.rhs], self.RB, (self.residual_range, self.residual_range_dims), orthonormalize=True, product=self.product, riesz_representatives=self.riesz_representatives) except ImageCollectionError as e: self.logger.warning(f'Cannot compute range of {e.op}. Evaluation will be slow.') self.residual_range = False if self.residual_range is False: operator = project(self.operator, None, self.RB) return NonProjectedResidualOperator(operator, self.rhs, self.riesz_representatives, self.product) with self.logger.block('Projecting residual operator ...'): if self.riesz_representatives: operator = project(self.operator, self.residual_range, self.RB, product=None) # the product cancels out. rhs = project(self.rhs, self.residual_range, None, product=None) else: operator = project(self.operator, self.residual_range, self.RB, product=self.product) rhs = project(self.rhs, self.residual_range, None, product=self.product) return ResidualOperator(operator, rhs)
def test_project_to_subbasis(operator_with_arrays): op, mu, U, V = operator_with_arrays op_UV = project(op, V, U) np.random.seed(4711 + U.dim + len(V)) for dim_range in {None, 0, len(V) // 2, len(V)}: for dim_source in {None, 0, len(U) // 2, len(U)}: op_UV_sb = project_to_subbasis(op_UV, dim_range, dim_source) assert op_UV_sb.range.dim == (op_UV.range.dim if dim_range is None else dim_range) assert op_UV_sb.source.dim == (op_UV.source.dim if dim_source is None else dim_source) range_basis = V if dim_range is None else V[:dim_range] source_basis = U if dim_source is None else U[:dim_source] op_UV_sb2 = project(op, range_basis, source_basis) u = op_UV_sb2.source.make_array(np.random.random( len(source_basis))) assert np.all( almost_equal(op_UV_sb.apply(u, mu=mu), op_UV_sb2.apply(u, mu=mu)))
def reduce(self): if self.residual_range is not False: with self.logger.block('Estimating residual range ...'): try: self.residual_range, self.residual_range_dims = \ estimate_image_hierarchical([self.operator, self.mass], [self.rhs], self.RB, (self.residual_range, self.residual_range_dims), orthonormalize=True, product=self.product, riesz_representatives=True) except ImageCollectionError as e: self.logger.warning(f'Cannot compute range of {e.op}. Evaluation will be slow.') self.residual_range = False if self.residual_range is False: operator = project(self.operator, None, self.RB) mass = project(self.mass, None, self.RB) return NonProjectedImplicitEulerResidualOperator(operator, mass, self.rhs, self.dt, self.product) with self.logger.block('Projecting residual operator ...'): operator = project(self.operator, self.residual_range, self.RB, product=None) # the product always cancels out. mass = project(self.mass, self.residual_range, self.RB, product=None) rhs = project(self.rhs, self.residual_range, None, product=None) return ImplicitEulerResidualOperator(operator, mass, rhs, self.dt)
def reduce(self): if self.residual_range is not False: with self.logger.block('Estimating residual range ...'): try: self.residual_range, self.residual_range_dims = \ estimate_image_hierarchical([self.operator], [self.rhs], self.RB, (self.residual_range, self.residual_range_dims), orthonormalize=True, product=self.product, riesz_representatives=self.riesz_representatives) except ImageCollectionError as e: self.logger.warning('Cannot compute range of {}. Evaluation will be slow.'.format(e.op)) self.residual_range = False if self.residual_range is False: operator = project(self.operator, None, self.RB) return NonProjectedResidualOperator(operator, self.rhs, self.riesz_representatives, self.product) with self.logger.block('Projecting residual operator ...'): if self.riesz_representatives: operator = project(self.operator, self.residual_range, self.RB, product=None) # the product cancels out. rhs = project(self.rhs, self.residual_range, None, product=None) else: operator = project(self.operator, self.residual_range, self.RB, product=self.product) rhs = project(self.rhs, self.residual_range, None, product=self.product) return ResidualOperator(operator, rhs)
def reduce(self): if self.residual_range is not False: with self.logger.block('Estimating residual range ...'): try: self.residual_range, self.residual_range_dims = \ estimate_image_hierarchical([self.operator, self.mass], [self.functional.T], self.RB, (self.residual_range, self.residual_range_dims), orthonormalize=True, product=self.product, riesz_representatives=True) except ImageCollectionError as e: self.logger.warning('Cannot compute range of {}. Evaluation will be slow.'.format(e.op)) self.residual_range = False if self.residual_range is False: operator = project(self.operator, None, self.RB) mass = project(self.mass, None, self.RB) return NonProjectedImplicitEulerResidualOperator(operator, mass, self.functional, self.dt, self.product) with self.logger.block('Projecting residual operator ...'): operator = project(self.operator, self.residual_range, self.RB, product=None) # the product always cancels out. mass = project(self.mass, self.residual_range, self.RB, product=None) functional = project(self.functional, None, self.residual_range, product=None) return ImplicitEulerResidualOperator(operator, mass, functional, self.dt)
def project_operators(self): fom = self.fom RB = self.bases['RB'] projected_operators = {'operator': project(fom.operator, RB, RB), 'rhs': project(fom.rhs, RB, None), 'products': {k: project(v, RB, RB) for k, v in fom.products.items()}, 'outputs': {k: project(v, None, RB) for k, v in fom.outputs.items()}} return projected_operators
def project_operators(self): fom = self.fom RB = self.bases['RB'] projected_operators = {'operator': project(fom.operator, RB, RB), 'rhs': project(fom.rhs, RB, None), 'products': {k: project(v, RB, RB) for k, v in fom.products.items()}, 'outputs': {k: project(v, None, RB) for k, v in fom.outputs.items()}} return projected_operators
def project_operators(self): fom = self.fom W = self.bases['W'] V = self.bases['V'] projected_operators = {'A': project(fom.A, W, V), 'B': project(fom.B, W, None), 'C': project(fom.C, None, V), 'D': fom.D, 'E': None if self.E_biorthonormal else project(fom.E, W, V)} return projected_operators
def project_operators(self): fom = self.fom W = self.bases['W'] V = self.bases['V'] projected_operators = {'A': project(fom.A, W, V), 'B': project(fom.B, W, None), 'C': project(fom.C, None, V), 'D': fom.D, 'E': None if self.E_biorthonormal else project(fom.E, W, V)} return projected_operators
def project_output(self): output_functional = self.fom.output_functional_dict li_part = output_functional['linear_part'] bi_part = output_functional['bilinear_part'] d_u_li_part = output_functional['d_u_linear_part'] d_u_bi_part = output_functional['d_u_bilinear_part'] RB = self.RBPrimal projected_functionals = { 'output_coefficient': output_functional['output_coefficient'], 'linear_part': project(li_part, RB, None), 'bilinear_part': project(bi_part, RB, RB), 'd_u_linear_part': project(d_u_li_part, RB, None), 'd_u_bilinear_part': project(d_u_bi_part, RB, RB), 'dual_projected_d_u_bilinear_part': project(d_u_bi_part, RB, self.RBDual), 'primal_dual_projected_op': project(self.fom.primal_model.operator, RB, self.RBDual), 'dual_projected_rhs': project(self.fom.primal_model.rhs, self.RBDual, None), 'primal_projected_dual_rhs': project(self.dual_intermediate_fom.rhs, RB, None), } return projected_functionals
def test_project_with_product_2(operator_with_arrays_and_products): op, mu, U, V, sp, rp = operator_with_arrays_and_products op_U = project(op, None, U) op_V = project(op, V, None, product=rp) op_U_V = project(op_U, V, None, product=rp) op_V_U = project(op_V, None, U) op_UV = project(op, V, U, product=rp) np.random.seed(4711 + U.dim + len(V)) W = op_UV.source.make_array(np.random.random(len(U))) Y0 = op_UV.apply(W, mu=mu) Y1 = op_U_V.apply(W, mu=mu) Y2 = op_V_U.apply(W, mu=mu) assert np.all(almost_equal(Y0, Y1)) assert np.all(almost_equal(Y0, Y2))
def test_project_with_product_2(operator_with_arrays_and_products): op, mu, U, V, sp, rp = operator_with_arrays_and_products op_U = project(op, None, U) op_V = project(op, V, None, product=rp) op_U_V = project(op_U, V, None, product=rp) op_V_U = project(op_V, None, U) op_UV = project(op, V, U, product=rp) np.random.seed(4711 + U.dim + len(V)) W = op_UV.source.make_array(np.random.random(len(U))) Y0 = op_UV.apply(W, mu=mu) Y1 = op_U_V.apply(W, mu=mu) Y2 = op_V_U.apply(W, mu=mu) assert np.all(almost_equal(Y0, Y1)) assert np.all(almost_equal(Y0, Y2))
def test_lincomb_op(): p1 = MonomOperator(1) p2 = MonomOperator(2) p12 = p1 + p2 p0 = p1 - p1 x = np.linspace(-1., 1., num=3) vx = p1.source.make_array((x[:, np.newaxis])) one = p1.source.make_array([1]) assert np.allclose(p0.apply(vx).to_numpy(), [0.]) assert np.allclose(p12.apply(vx).to_numpy(), (x * x + x)[:, np.newaxis]) assert np.allclose((p1 * 2.).apply(vx).to_numpy(), (x * 2.)[:, np.newaxis]) with pytest.raises(AssertionError): p2.jacobian(vx) for i in range(len(vx)): assert almost_equal( p2.jacobian(vx[i]).apply(one), p1.apply(vx[i]) * 2.) assert almost_equal(p0.jacobian(vx[i]).apply(one), vx[i] * 0.) with pytest.raises(TypeError): p2.as_vector() p1.as_vector() assert almost_equal(p1.as_vector(), p1.apply(p1.source.make_array([1.]))) basis = p1.source.make_array([1.]) for p in (p1, p2, p12): projected = project(p, basis, basis) pa = projected.apply(vx) assert almost_equal(pa, p.apply(vx)).all()
def test_project_with_product(operator_with_arrays_and_products): op, mu, U, V, sp, rp = operator_with_arrays_and_products op_UV = project(op, V, U, product=rp) np.random.seed(4711 + U.dim + len(V)) coeffs = np.random.random(len(U)) X = op_UV.apply(op_UV.source.make_array(coeffs), mu=mu) Y = op_UV.range.make_array(rp.apply2(op.apply(U.lincomb(coeffs), mu=mu), V)) assert np.all(almost_equal(X, Y))
def test_project_with_product(operator_with_arrays_and_products): op, mu, U, V, sp, rp = operator_with_arrays_and_products op_UV = project(op, V, U, product=rp) np.random.seed(4711 + U.dim + len(V)) coeffs = np.random.random(len(U)) X = op_UV.apply(op_UV.source.make_array(coeffs), mu=mu) Y = op_UV.range.make_array(rp.apply2(op.apply(U.lincomb(coeffs), mu=mu), V)) assert np.all(almost_equal(X, Y))
def test_project(operator_with_arrays): op, mu, U, V = operator_with_arrays op_UV = project(op, V, U) np.random.seed(4711 + U.dim + len(V)) coeffs = np.random.random(len(U)) X = op_UV.apply(op_UV.source.make_array(coeffs), mu=mu) Y = op_UV.range.make_array(V.dot(op.apply(U.lincomb(coeffs), mu=mu)).T) assert np.all(almost_equal(X, Y))
def test_project(operator_with_arrays): op, mu, U, V = operator_with_arrays op_UV = project(op, V, U) np.random.seed(4711 + U.dim + len(V)) coeffs = np.random.random(len(U)) X = op_UV.apply(op_UV.source.make_array(coeffs), mu=mu) Y = op_UV.range.make_array(V.dot(op.apply(U.lincomb(coeffs), mu=mu)).T) assert np.all(almost_equal(X, Y))
def project_operators(self): fom = self.fom RB = self.bases['RB'] product = self.products['RB'] if self.initial_data_product != product: # TODO there should be functionality for this somewhere else projection_matrix = RB.gramian(self.initial_data_product) projection_op = NumpyMatrixOperator(projection_matrix) inverse_projection_op = InverseOperator(projection_op, 'inverse_projection_op') pid = project(fom.initial_data, range_basis=RB, source_basis=None, product=self.initial_data_product) projected_initial_data = Concatenation([inverse_projection_op, pid]) else: projected_initial_data = project(fom.initial_data, range_basis=RB, source_basis=None, product=product) projected_operators = {'mass': (None if fom.mass is None or self.product_is_mass else project(fom.mass, RB, RB)), 'operator': project(fom.operator, RB, RB), 'rhs': project(fom.rhs, RB, None) if fom.rhs is not None else None, 'initial_data': projected_initial_data, 'products': {k: project(v, RB, RB) for k, v in fom.products.items()}, 'outputs': {k: project(v, None, RB) for k, v in fom.outputs.items()}} return projected_operators
def project_operators(self): fom = self.fom RB = self.bases['RB'] product = self.products['RB'] if self.initial_data_product != product: # TODO there should be functionality for this somewhere else projection_matrix = RB.gramian(self.initial_data_product) projection_op = NumpyMatrixOperator(projection_matrix) inverse_projection_op = InverseOperator(projection_op, 'inverse_projection_op') pid = project(fom.initial_data, range_basis=RB, source_basis=None, product=self.initial_data_product) projected_initial_data = ConcatenationOperator([inverse_projection_op, pid]) else: projected_initial_data = project(fom.initial_data, range_basis=RB, source_basis=None, product=product) projected_operators = { 'mass': (None if (isinstance(fom.mass, IdentityOperator) and product is None or self.product_is_mass) else project(fom.mass, RB, RB)), 'operator': project(fom.operator, RB, RB), 'rhs': project(fom.rhs, RB, None), 'initial_data': projected_initial_data, 'products': {k: project(v, RB, RB) for k, v in fom.products.items()}, 'output_functional': project(fom.output_functional, None, RB) if fom.output_functional else None } return projected_operators
def test_project_to_subbasis_no_source_basis(operator_with_arrays): op, mu, U, V = operator_with_arrays op_V = project(op, V, None) np.random.seed(4711 + U.dim + len(V)) for dim_range in {None, 0, len(V) // 2, len(V)}: op_V_sb = project_to_subbasis(op_V, dim_range, None) assert op_V_sb.range.dim == (op_V.range.dim if dim_range is None else dim_range) assert op_V_sb.source == op_V.source range_basis = V if dim_range is None else V[:dim_range] op_V_sb2 = project(op, range_basis, None) assert np.all( almost_equal(op_V_sb.apply(U, mu=mu), op_V_sb2.apply(U, mu=mu)))
def assemble(self, mu=None): op = self.operator.assemble(mu=mu) if op == self.operator: # avoid infinite recursion in apply_inverse default impl return self from pymor.algorithms.projection import project pop = project(op, range_basis=self.range_basis, source_basis=self.source_basis, product=self.product) if self.solver_options: pop = pop.with_(solver_options=self.solver_options) return pop
def project_hessian(self): RBP = self.RBPrimal RBD = self.RBDual fom = self.fom.primal_model output_functional = self.fom.output_functional_dict linear_part = output_functional['d_u_linear_part'] bilinear_part = output_functional['d_u_bilinear_part'] projected_hessian = {} for (key, size) in sorted(self.primal_fom.parameters.items()): hessian = np.empty(size, dtype=dict) for l in range(size): D_rhs = project(fom.rhs.d_mu(key, l), RBD, None) P_D_op = project(fom.operator.d_mu(key, l), RBP, RBD) proj_hessian = { 'D_rhs': D_rhs, 'P_D_op': PD_op, } hessian[l] = proj_hessian projected_hessian[key] = hessian return projected_hessian
def reduce(self): # Note that it is possible that rhs.source == rhs.range, nameley if both # are one-dimensional NumpyVectorSpaces which agree with the range of # operator. Usually, this should not happen, since at least one of these # spaces should have an id which is different from the id of operator.range. # However, even if it happens but rhs is actually a vector, we are on # the safe side, since first computing the Riesz representatives does not # change anything in one-dimensional spaces, and it does not matter whether # we project from the left or from the right. rhs_is_functional = (self.rhs.source == self.operator.range) if self.residual_range is not False: with self.logger.block('Estimating residual range ...'): try: self.residual_range, self.residual_range_dims = \ estimate_image_hierarchical([self.operator], [self.rhs.T if rhs_is_functional else self.rhs], self.RB, (self.residual_range, self.residual_range_dims), orthonormalize=True, product=self.product, riesz_representatives=rhs_is_functional) except ImageCollectionError as e: self.logger.warning('Cannot compute range of {}. Evaluation will be slow.'.format(e.op)) self.residual_range = False if self.residual_range is False: operator = project(self.operator, None, self.RB) return NonProjectedResidualOperator(operator, self.rhs, rhs_is_functional, self.product) with self.logger.block('Projecting residual operator ...'): if rhs_is_functional: operator = project(self.operator, self.residual_range, self.RB, product=None) # the product cancels out. rhs = project(self.rhs, None, self.residual_range, product=None) else: operator = project(self.operator, self.residual_range, self.RB, product=self.product) rhs = project(self.rhs, self.residual_range, None, product=self.product) return ResidualOperator(operator, rhs, rhs_is_functional)
def jacobian(self, U, mu=None): if self.linear: return self.assemble(mu) assert len(U) == 1 mu = self.parse_parameter(mu) if self.source_basis is None: J = self.operator.jacobian(U, mu=mu) else: J = self.operator.jacobian(self.source_basis.lincomb(U.to_numpy()), mu=mu) from pymor.algorithms.projection import project pop = project(J, range_basis=self.range_basis, source_basis=self.source_basis, product=self.product, name=self.name + '_jacobian') if self.solver_options: options = self.solver_options.get('jacobian') if options: pop = pop.with_(solver_options=options) return pop
def project_operators(self): fom = self.fom W = self.bases['W'] V = self.bases['V'] projected_operators = {'M': None if self.M_biorthonormal else project(fom.M, W, V), 'E': project(fom.E, W, V), 'K': project(fom.K, W, V), 'B': project(fom.B, W, None), 'Cp': project(fom.Cp, None, V), 'Cv': project(fom.Cv, None, V), 'D': fom.D} return projected_operators
def project_operators(self): fom = self.fom W = self.bases['W'] V = self.bases['V'] projected_operators = {'M': None if self.M_biorthonormal else project(fom.M, W, V), 'E': project(fom.E, W, V), 'K': project(fom.K, W, V), 'B': project(fom.B, W, None), 'Cp': project(fom.Cp, None, V), 'Cv': project(fom.Cv, None, V), 'D': fom.D} return projected_operators
def test_lincomb_op(): p1 = MonomOperator(1) p2 = MonomOperator(2) p12 = p1 + p2 p0 = p1 - p1 x = np.linspace(-1., 1., num=3) vx = p1.source.make_array((x[:, np.newaxis])) assert np.allclose(p0.apply(vx).to_numpy(), [0.]) assert np.allclose(p12.apply(vx).to_numpy(), (x * x + x)[:, np.newaxis]) assert np.allclose((p1 * 2.).apply(vx).to_numpy(), (x * 2.)[:, np.newaxis]) assert almost_equal(p2.jacobian(vx).apply(vx), p1.apply(vx) * 2.).all() assert almost_equal(p0.jacobian(vx).apply(vx), vx * 0.).all() with pytest.raises(TypeError): p2.as_vector() p1.as_vector() assert almost_equal(p1.as_vector(), p1.apply(p1.source.make_array([1.]))) basis = p1.source.make_array([1.]) for p in (p1, p2, p12): projected = project(p, basis, basis) pa = projected.apply(vx) assert almost_equal(pa, p.apply(vx)).all()
def project_operator(k, op): return project(op, range_basis=RB if RB in op.range else None, source_basis=RB if RB in op.source else None, product=self.product if k in self.orthogonal_projection else None)
def reduce(self, r, projection='bfsr'): """Reduce using SOBT. Parameters ---------- r Order of the reduced model. projection Projection method used: - `'sr'`: square root method - `'bfsr'`: balancing-free square root method (default, since it avoids scaling by singular values and orthogonalizes the projection matrices, which might make it more accurate than the square root method) - `'biorth'`: like the balancing-free square root method, except it biorthogonalizes the projection matrices Returns ------- rom Reduced-order |SecondOrderModel|. """ assert 0 < r < self.fom.order assert projection in ('sr', 'bfsr', 'biorth') # compute all necessary Gramian factors pcf = self.fom.gramian('pc_lrcf', mu=self.mu) pof = self.fom.gramian('po_lrcf', mu=self.mu) vcf = self.fom.gramian('vc_lrcf', mu=self.mu) vof = self.fom.gramian('vo_lrcf', mu=self.mu) if r > min(len(pcf), len(pof), len(vcf), len(vof)): raise ValueError( 'r needs to be smaller than the sizes of Gramian factors.') # find necessary SVDs Up, sp, Vp = spla.svd(pof.inner(pcf), lapack_driver='gesvd') Up = Up.T Uv, sv, Vv = spla.svd(vof.inner(vcf, product=self.fom.M), lapack_driver='gesvd') Uv = Uv.T # compute projection matrices and find the reduced model self.V1 = pcf.lincomb(Vp[:r]) self.W1 = pof.lincomb(Up[:r]) self.V2 = vcf.lincomb(Vv[:r]) self.W2 = vof.lincomb(Uv[:r]) if projection == 'sr': alpha1 = 1 / np.sqrt(sp[:r]) self.V1.scal(alpha1) self.W1.scal(alpha1) alpha2 = 1 / np.sqrt(sv[:r]) self.V2.scal(alpha2) self.W2.scal(alpha2) W1TV1invW1TV2 = self.W1.inner(self.V2) projected_ops = {'M': IdentityOperator(NumpyVectorSpace(r))} elif projection == 'bfsr': gram_schmidt(self.V1, atol=0, rtol=0, copy=False) gram_schmidt(self.W1, atol=0, rtol=0, copy=False) gram_schmidt(self.V2, atol=0, rtol=0, copy=False) gram_schmidt(self.W2, atol=0, rtol=0, copy=False) W1TV1invW1TV2 = spla.solve(self.W1.inner(self.V1), self.W1.inner(self.V2)) projected_ops = { 'M': project(self.fom.M, range_basis=self.W2, source_basis=self.V2) } elif projection == 'biorth': gram_schmidt_biorth(self.V1, self.W1, copy=False) gram_schmidt_biorth(self.V2, self.W2, product=self.fom.M, copy=False) W1TV1invW1TV2 = self.W1.inner(self.V2) projected_ops = {'M': IdentityOperator(NumpyVectorSpace(r))} projected_ops.update({ 'E': project(self.fom.E.assemble(mu=self.mu), range_basis=self.W2, source_basis=self.V2), 'K': project(self.fom.K.assemble(mu=self.mu), range_basis=self.W2, source_basis=self.V1.lincomb(W1TV1invW1TV2.T)), 'B': project(self.fom.B.assemble(mu=self.mu), range_basis=self.W2, source_basis=None), 'Cp': project(self.fom.Cp.assemble(mu=self.mu), range_basis=None, source_basis=self.V1.lincomb(W1TV1invW1TV2.T)), 'Cv': project(self.fom.Cv.assemble(mu=self.mu), range_basis=None, source_basis=self.V2), 'D': self.fom.D.assemble(mu=self.mu), }) rom = SecondOrderModel(name=self.fom.name + '_reduced', **projected_ops) rom.disable_logging() return rom
def project_operator(k, op): return project(op, range_basis=RB if RB in op.range else None, source_basis=RB if RB in op.source else None, product=self.product if k in self.orthogonal_projection else None)
def project_product(self): projected_product = project(self.opt_product, self.RBPrimal, self.RBPrimal) return projected_product
def reduce(self, r, projection='bfsr'): """Reduce using SOBT. Parameters ---------- r Order of the reduced model. projection Projection method used: - `'sr'`: square root method - `'bfsr'`: balancing-free square root method (default, since it avoids scaling by singular values and orthogonalizes the projection matrices, which might make it more accurate than the square root method) - `'biorth'`: like the balancing-free square root method, except it biorthogonalizes the projection matrices Returns ------- rd Reduced system. """ assert 0 < r < self.d.n assert projection in ('sr', 'bfsr', 'biorth') # compute all necessary Gramian factors pcf = self.d.gramian('pcf') pof = self.d.gramian('pof') vcf = self.d.gramian('vcf') vof = self.d.gramian('vof') if r > min(len(pcf), len(pof), len(vcf), len(vof)): raise ValueError('r needs to be smaller than the sizes of Gramian factors.') # find necessary SVDs Up, sp, Vp = spla.svd(pof.inner(pcf)) Up = Up.T Uv, sv, Vv = spla.svd(vof.inner(vcf, product=self.d.M)) Uv = Uv.T # compute projection matrices and find the reduced model self.V1 = pcf.lincomb(Vp[:r]) self.W1 = pof.lincomb(Up[:r]) self.V2 = vcf.lincomb(Vv[:r]) self.W2 = vof.lincomb(Uv[:r]) if projection == 'sr': alpha1 = 1 / np.sqrt(sp[:r]) self.V1.scal(alpha1) self.W1.scal(alpha1) alpha2 = 1 / np.sqrt(sv[:r]) self.V2.scal(alpha2) self.W2.scal(alpha2) W1TV1invW1TV2 = self.W1.inner(self.V2) projected_ops = {'M': IdentityOperator(NumpyVectorSpace(r, self.d.state_space.id))} elif projection == 'bfsr': self.V1 = gram_schmidt(self.V1, atol=0, rtol=0) self.W1 = gram_schmidt(self.W1, atol=0, rtol=0) self.V2 = gram_schmidt(self.V2, atol=0, rtol=0) self.W2 = gram_schmidt(self.W2, atol=0, rtol=0) W1TV1invW1TV2 = spla.solve(self.W1.inner(self.V1), self.W1.inner(self.V2)) projected_ops = {'M': project(self.d.M, range_basis=self.W2, source_basis=self.V2)} elif projection == 'biorth': self.V1, self.W1 = gram_schmidt_biorth(self.V1, self.W1) self.V2, self.W2 = gram_schmidt_biorth(self.V2, self.W2, product=self.d.M) W1TV1invW1TV2 = self.W1.inner(self.V2) projected_ops = {'M': IdentityOperator(NumpyVectorSpace(r, self.d.state_space.id))} projected_ops.update({'E': project(self.d.E, range_basis=self.W2, source_basis=self.V2), 'K': project(self.d.K, range_basis=self.W2, source_basis=self.V1.lincomb(W1TV1invW1TV2.T)), 'B': project(self.d.B, range_basis=self.W2, source_basis=None), 'Cp': project(self.d.Cp, range_basis=None, source_basis=self.V1.lincomb(W1TV1invW1TV2.T)), 'Cv': project(self.d.Cv, range_basis=None, source_basis=self.V2)}) rd = self.d.with_(operators=projected_ops, visualizer=None, estimator=None, cache_region=None, name=self.d.name + '_reduced') rd.disable_logging() return rd