Spaces¶
Vectors and vector spaces¶
RegPy uses duck typing for vectors. A vector may be any object that implements
vector addition and subtraction, scalar multiplication and division, unary
negation, and the corresponding in-place operations. Generic algorithms must
not assume that vectors are NumPy arrays or that they support indexing,
componentwise operations, attributes such as shape or dtype, or a
copy method.
Every vector representation is accompanied by a subclass of
regpy.vecsps.VectorSpaceBase. The vector space recognizes its vectors
and provides representation-dependent operations such as allocation, copying,
inner products, random sampling, flattening, and reconstruction. Generic code
should therefore use, for example, space.empty() together with
space.copyto(target, source) instead of calling source.copy().
Vector operations and storage¶
The distinction between a vector and its vector space is deliberate. The
vector itself supplies only linear arithmetic. The accompanying
VectorSpaceBase instance owns operations whose implementation depends on
the representation or storage layout. Its most important public operations
include:
x in spacetests whetherxis a vector of the space;space.zeros(),space.ones(), andspace.empty()allocate vectors;space.copyto(target, source)copies values without replacingtarget;space.vdot(x, y)supplies the standard pairing used by RegPy;space.rand()andspace.randn()construct sample vectors; andspace.flatten(x)andspace.fromflat(values)convert between the representation and real coordinate vectors when the concrete space supports those conversions.
Allocation with empty does not initialize the vector. Code using it must
completely overwrite the result, usually through copyto or an in-place
operator call. This avoids unnecessary initialization for vector types whose
storage is expensive.
For example, the following generic function uses no NumPy-specific operation:
def linear_combination(space, x, y, a, b):
if x not in space or y not in space:
raise ValueError("x and y must belong to space")
result = space.empty()
space.copyto(result, x)
result *= a
result += b * y
return result
Methods such as flatten are intentionally supplied by the space rather
than required from vectors. An algorithm that converts to coordinates is no
longer purely representation-independent and should use these methods
explicitly.
Specialized vector spaces may provide additional operations. NumPy vector spaces support array operations, while NGSolve vector spaces expose operations appropriate for their finite-element representation. Algorithms that require such capabilities should state and validate the corresponding restriction.
All vector spaces are interpreted as spaces over the real numbers, including spaces whose vectors have complex coefficients. This convention determines the real dimension and the standard inner product used to define operator adjoints, subgradients, Hessians, and conjugate functionals. See Functionals for the corresponding dual-pairing convention.
Dimensions and metadata¶
A vector space may expose representation metadata even when its vectors do
not. In particular, space.shape describes the coefficient layout,
space.ndim its number of axes, and space.size its number of
coefficients. The property space.realsize gives the dimension after the
space is interpreted over the real numbers. Consequently, a space with
space.is_complex set to true normally has twice as many real dimensions as
complex coefficients.
Generic algorithms should query this metadata from the vector space and only when it is mathematically required. They must not infer it from attributes on an arbitrary vector object.
Direct sums¶
Several vector spaces can be combined into a
regpy.vecsps.DirectSum. Addition provides a convenient construction:
product_space = parameter_space + auxiliary_space
combined = product_space.join(parameter, auxiliary)
parameter, auxiliary = product_space.split(combined)
Allocation, copying, membership tests, pairings, and random sampling are then performed componentwise by the corresponding summand. Direct sums allow operators and solvers to work with coupled unknowns whose components may even use different vector representations.
Hilbert spaces¶
A regpy.hilbert.HilbertSpace equips a vector space with an inner
product. Its vecsp attribute is the underlying
regpy.vecsps.VectorSpaceBase; the Hilbert space adds geometry without
changing the vectors or their storage. If its Gram operator is denoted by
\(G_X\), the inner product is
where the pairing on the right is supplied by the underlying vector space. This separates the vector representation from the geometry used by an inverse problem or optimization method.
Gram operators and norms¶
The property space.gram is a linear operator from space.vecsp to
itself. It represents the inner product relative to the standard vector-space
pairing and is required to be positive and self-adjoint in the corresponding
sense. The main Hilbert-space operations are:
space.inner(x, y)evaluates the inner product;space.norm(x)evaluates the induced norm;space.gram(x)applies the Riesz map represented by the Gram operator;space.gram_inv(xstar)applies its inverse; andspace.norm_functionalreturns the corresponding one-half squared-norm functional.
These operations should be used instead of explicitly assembling a Gram matrix. In many applications the Gram operator is a multiplication, differential, integral, or fast-transform-based operator whose action is much cheaper than forming a dense matrix.
The Gram inverse is obtained from space.gram.inverse when the Gram operator
provides an inverse; otherwise the concrete Hilbert space can implement
gram_inv separately. Some spaces also provide space.cholesky, a
Cholesky-type factor or square root of the Gram operator. This factor is useful,
for example, when generating white Gaussian noise in the Hilbert-space
geometry.
Adjoints and dual Hilbert spaces¶
An operator’s adjoint is always defined with respect to the standard
pairings of its vector spaces. If an operator \(T\colon :mathbb{X}\to \mathbb{Y}\) is viewed
between Hilbert spaces with Gram operators \(G_X:\mathbb{X}\to \mathbb{X}'\)
and \(G_Y:\mathbb{Y}\to\mathbb{Y}'\), its
Hilbert-space adjoint is obtained by
where \(T'\) is the standard RegPy adjoint.
The method space.dual_space() returns the Hilbert structure on the same
underlying vector space for which the roles of gram and gram_inv are
interchanged. This is useful for dual norms, subgradient methods, and conjugate
functionals. Direct sums of Hilbert spaces can be formed by adding them; their
inner products and Gram operators act componentwise, with optional positive
weights.
Abstract Hilbert spaces¶
RegPy provides abstract Hilbert spaces that select a concrete implementation
for a given vector-space class. For example, Sobolev dispatches to the
implementation registered for the type of my_domain:
from regpy.hilbert import Sobolev
h1_on_my_space = Sobolev(my_domain, index=1)
Arguments can also be stored in an abstract-space factory and applied later:
h2 = Sobolev(index=2)
h2_on_my_space = h2(my_domain)
This dispatch mechanism makes algorithms reusable across compatible vector
representations while allowing specialized implementations where necessary.
If no implementation is registered for the vector-space type and requested
parameters, construction raises NotImplementedError. The analogous
mechanism for abstract functionals is described in Functionals.