FUTON, bases and combiners¶
The FUTON concept page derives the model; this page is the reference for inro.models.futon: FUTON and its encoding, FUTONEncoding, the bases and combiners, and the SharedRows and Taps features its bases return on grids and for local bases.
futon
¶
FUTON: a fixed basis per axis, a tensor-network combiner and a head.
FUTON computes output_activation(head(combiner(basis(x)))) for coordinates
(N, C) in [-1, 1]:
- A basis gives each axis
cfeatures(N, K_c). Every basis takesin_features, the number of axesC;num_components, the widthsK_c, one for all axes or one per axis;normalize, whether to L2-normalize each feature vector (defaultTrue); andgrid_size, the sizes of the gridlinspace(-1, 1, size)all inputs lie on, if any, whose features it tabulates once. - A combiner fuses the axes with a tensor network, stated once as an einsum
equation over the axes' projections, into
(N, R)features. - A head maps them to the signal:
"linear","mlp"or"identity".
Points share the features of their shared coordinates. On a grid, a basis gives each axis
as SharedRows, a table of its coordinates and every point's row in it, so a
combiner projects S_c rows rather than N points, and it returns its features as a
GridFeatures, its equation and operands, which the head's first linear layer reads
off the whole grid in one einsum with its weight when the batch covers the grid. A kernel
of compact support gives scattered points only their nonzero features, as Taps.
Coordinates that need gradients are evaluated point by point.
SharedRows
¶
Features of one axis on a grid: a table of S rows and each point's row.
Behaves as the (N, K_c) matrix table[rows], which dense returns. A
product with a factor projects the S rows rather than N points, and the
product shares the rows still.
Attributes:
-
table(Tensor) –Features
(S, K_c)of the axis'sScoordinates. -
rows(Tensor) –Row of each of the
Npoints.
unflatten
¶
unflatten(dim: int, sizes: Sequence[int]) -> SharedRows
Unflatten a dimension of the features, as torch.Tensor.unflatten.
Taps
¶
Sparse features of one axis: the L nonzero values of each coordinate.
Behaves as the (N, K_c) matrix whose row n holds values[n] in columns
start[n] to start[n] + L - 1.
Attributes:
-
values(Tensor) –Nonzero features
(N, L). -
start(Tensor) –First column
(N,)of each row's window. -
compiled(bool) –Whether a compiled graph made the taps, whose product then gathers windows of rows. Set where the taps are made: a product traced after a graph break may no longer see
torch.compiler.is_compiling().
CosineBasis
¶
CosineBasis(in_features: int, num_components: Sizes, normalize: bool = True, grid_size: Sizes | None = None)
Cosines \(\cos(k \pi u)\) with \(u = (x + 1) / 2\), of frequencies \(k = 0, \dots, K_c - 1\): the paper's basis without its \(\sqrt{2}\), which the factors absorb.
Source code in inro/models/futon.py
SincBasis
¶
SincBasis(in_features: int, num_components: Sizes, normalize: bool = True, grid_size: Sizes | None = None)
Cardinal sines \(\operatorname{sinc}((x - \mu_k) / w)\) centered on \(\mu_k = -1 + k w\), spaced by \(w = 2 / (K_c - 1)\).
Each component is one at its own center and zero at the others, so
normalize=False gives cardinal (Whittaker) interpolation.
Source code in inro/models/futon.py
ChebyshevBasis
¶
ChebyshevBasis(in_features: int, num_components: Sizes, normalize: bool = True, grid_size: Sizes | None = None)
Chebyshev polynomials \(T_k(x) = \cos(k \arccos x)\) of the first kind, orthogonal under the weight \((1 - x^2)^{-1/2}\).
Source code in inro/models/futon.py
LegendreBasis
¶
LegendreBasis(in_features: int, num_components: Sizes, normalize: bool = True, grid_size: Sizes | None = None)
Legendre polynomials, the standard, unscaled ones, as fixed combinations of Chebyshev polynomials with nonnegative coefficients that sum to one.
Source code in inro/models/futon.py
TriangleBasis
¶
TriangleBasis(in_features: int, num_components: Sizes, normalize: bool = True, grid_size: Sizes | None = None)
Triangles (hats) \(\max(0, 1 - |x - \mu_k| / w)\) centered on
\(\mu_k = -1 + k w\), spaced by \(w = 2 / (K_c - 1)\): piecewise-linear
interpolation, two components nonzero, K_c >= 2.
With normalize=False, a CP combiner and an MLP head give the TensoRF-CP
parametrization: a linear map of these features samples a 1D feature grid.
References
Chen et al., "TensoRF: Tensorial Radiance Fields", ECCV 2022.
Source code in inro/models/futon.py
LanczosBasis
¶
LanczosBasis(in_features: int, num_components: Sizes, radius: int = 3, normalize: bool = True, grid_size: Sizes | None = None)
Lanczos kernels, windowed sines centered on \(\mu_k = -1 + k w\), spaced by
\(w = 2 / (K_c - 1)\), K_c >= 2 * radius.
With offset \(t = (x - \mu_k) / w\) and radius \(a\), the kernel is
\(\operatorname{sinc}(t) \operatorname{sinc}(t / a)\) for \(|t| < a\) and
zero elsewhere: one at its own center, zero at the others, and at most 2 * a
components nonzero.
Parameters:
-
radius(int, default:3) –Kernel radius in grid steps:
2gives Lanczos-2 (four taps),3Lanczos-3 (six taps).
Source code in inro/models/futon.py
GridFeatures
¶
A combiner's features on a grid, its network not yet contracted.
torch.nn.functional.linear, as a head's first layer calls it, contracts the
network with the layer's weight in one einsum over the axes' tables when the grid
has at most 64 / out_features cells per point, which spares every point's
features and the scatter of their gradients. A sparser batch, and every other
torch function, take the features point by point, dense.
Source code in inro/models/futon.py
dense
¶
CPCombiner
¶
Canonical polyadic (CP) combiner.
Projects each axis to R channels with its factor
\(\boldsymbol{U}^{(c)}\), factors[c] of shape (K_c, R), and multiplies
the projections elementwise, ...r,...r->...r (Eqs. 10 and 11 of the paper). A
linear head's weight is then the CP model's output factor \(\boldsymbol{V}\).
Parameters:
-
in_features(Sequence[int]) –Number of features
K_cof each axis. -
rank(int) –CP rank
R, the number of the network's channels.
References
Kolda and Bader, "Tensor Decompositions and Applications", SIAM Review 2009.
Source code in inro/models/futon.py
reset_parameters
¶
TRCombiner
¶
Tensor-ring (TR) combiner.
Axis c contracts its features with a core (R, K_c, R) into an R x R
matrix, and the matrices multiply in order, ...ab,...bc->...ac on two axes. The
product is flattened, R**2 wide, leaving the closure of the ring, such as a
trace, to what follows. Extends the paper's CP model.
Parameters:
-
in_features(Sequence[int]) –Number of features
K_cof each axis. -
rank(int) –Tensor-ring rank
R.
References
Zhao et al., "Tensor Ring Decomposition", arXiv 2016.
Source code in inro/models/futon.py
TuckerCombiner
¶
Tucker combiner.
Projects axis c to rank[c] channels with its factor (K_c, rank[c]) and
contracts the projections with a core (rank[0], ..., rank[C - 1], max(rank)),
such as ...b,abz,...a->...z on two axes whose second rank is the larger. With a
linear head the weight tensor is in Tucker format, and CP is the case of a
superdiagonal core. Extends the paper's CP model.
Parameters:
-
in_features(Sequence[int]) –Number of features
K_cof each axis. -
rank(Sizes) –Multilinear rank, shared or per axis; the network has as many channels as its largest.
References
Tucker, "Some mathematical notes on three-mode factor analysis", Psychometrika 1966.
Source code in inro/models/futon.py
reset_parameters
¶
Initialize the factors (fan-in K_c) and the core Kaiming-uniform.
FUTONEncoding
¶
FUTON's encoding, combiner(basis(x)), which FUTON decodes.
Parameters:
-
in_features(int) –Number of coordinate axes
C. -
basis(ModuleSpec) –Basis spec, built as
basis(in_features, **kwargs). A custom basis must exposenum_components, one count per axis. -
combiner(ModuleSpec) –Combiner spec, built as
combiner(basis.num_components, **kwargs). A custom combiner must exposeout_features.
Shape
- Input:
(N, C)coordinates in[-1, 1]. - Output:
(N, out_features).
Source code in inro/models/futon.py
FUTON
¶
FUTON(in_features: int, out_features: int, basis: ModuleSpec, combiner: ModuleSpec, head: ModuleSpec, output_activation: Activation | None = None)
Fourier Tensor Network for implicit neural representations.
Computes output_activation(head(combiner(basis(x)))). Each stage is a spec:
a module instance, a registry key, a module class, or a (key_or_class, kwargs)
pair. Registry keys:
- Bases:
"cosine","sinc","legendre","chebyshev","triangle","lanczos". - Combiners:
"cp","tr","tucker". - Heads:
"linear","mlp","identity".
Parameters:
-
in_features(int) –Number of coordinate axes
C. -
out_features(int) –Number of output channels
D. -
basis(ModuleSpec) –Basis spec; see
FUTONEncoding. -
combiner(ModuleSpec) –Combiner spec; see
FUTONEncoding. -
head(ModuleSpec) –Head spec, built as
head(encoding.out_features, out_features, **kwargs). -
output_activation(Activation | None, default:None) –Callable applied to the output;
Noneuses the identity.
Shape
- Input:
(..., C)coordinates in[-1, 1]. - Output:
(..., D).
Note
The cosine basis, CP combiner and linear head implement Eq. (9) of the paper:
encoding.combiner.factors[c] stores \(\boldsymbol{U}^{(c)}\) and
head.weight stores \(\boldsymbol{V}\). The other bases and combiners and
the MLP head are extensions.
Examples:
>>> model = FUTON(
... in_features=2,
... out_features=3,
... basis=("cosine", {"num_components": 256}),
... combiner=("cp", {"rank": 256}),
... head="linear",
... output_activation=torch.tanh,
... )
>>> model(torch.rand(64, 64, 2) * 2 - 1).shape
torch.Size([64, 64, 3])
Source code in inro/models/futon.py
Starts¶
futon_inits
¶
Starts of FUTON fitted to a signal, and the CP algebra its bounds and solvers share.
On a grid, a CP FUTON, a CP combiner with a linear head, is a CP tensor: its factor of
axis c is the axis's basis times the factor matrix, and its factor of the channels
is the head's weight. A start sets
these blocks of a built FUTON from the signal, in place, before training:
SVDInitgives theRcomponents theRheaviest rank-one terms of the signal's low frequencies, found by nested SVDs;ALSInitrefines those terms by alternating least squares first.
SVDInit
dataclass
¶
Start a FUTON from the heaviest rank-one terms of a signal's low frequencies.
Called on a built FUTON and a signal (N_1, ..., N_C, D) on the grid
linspace(-1, 1, N_c) of each axis, such as a coordinate dataset's target,
it sets the model's parameters in place:
- Project the signal onto the lowest
K_c // cutcosines of each axisc, normalized per point as inCosineBasis. - Expand the projection, a tensor of the
Caxes and the channels, into orthonormal rank-one terms by nested SVDs: the first splits off the shortest mode, and each next one the shortest mode left, which leaves the most terms. - Give the
Rheaviest terms, asrefinereturns them, to theRcomponents: a term's vector of axisc, fitted to the model's basis on the grid, becomes a column of factorc, and its vector of the channels a column of the output factor, the head's weight.
Each term's weight, times scale, is shared evenly by its component's factors
and output column. Components beyond the terms keep their draw behind a zero
output column, silent until trained, and a head bias starts at zero. The start
runs on the model's device, in its precision, and raises a ValueError for a
model that is not a CP FUTON.
Parameters:
-
cut(int, default:4) –Divisor of each axis's component count
K_cthat leaves the frequencies kept. -
scale(float, default:1.0) –Factor of the output before the activation. With
tanh,2starts a signal of-1and1near-0.96and0.96.
Examples:
>>> model = FUTON(
... in_features=2,
... out_features=3,
... basis=("sinc", {"num_components": 32}),
... combiner=("cp", {"rank": 16}),
... head="linear",
... )
>>> SVDInit(scale=2.0)(model, torch.rand(64, 64, 3) * 2 - 1)
refine
¶
Return the terms' CP factors as the start loads them: here, as they are.
Parameters:
-
coefficients(Tensor) –The projection
(K_1 // cut, ..., K_C // cut, D). -
factors(list[Tensor]) –CP factors
(I_m, T)of itsTheaviest terms, a factor per axis and then the channels', the terms' weights in the first.
Source code in inro/models/futon_inits.py
ALSInit
dataclass
¶
Start a FUTON as SVDInit does, its terms refined by ALS first.
Each sweep of alternating least squares solves every factor's least-squares problem
on the projection exactly, the channels' included, the others held, as
futon_bound refines its upper bound, so the
misfit never rises. A sweep works on the projection, not on the samples.
Parameters:
-
sweeps(int, default:10) –Most sweeps; they stop sooner once one lowers the misfit by less than
3e-5of it. -
cut(int, default:4) –See
SVDInit. -
scale(float, default:1.0) –See
SVDInit.
refine
¶
Return the factors after at most sweeps sweeps of ALS on the projection.
Solvers¶
A FUTON with a CP combiner and a linear head, both without bias, is multilinear: with the other blocks held, its output is linear in each factor matrix and in the head, so fitting one block is a least-squares problem. inro.LeastSquares and inro.MultiplicativeUpdate sweep the blocks once per batch, and inro.train takes them in place of Adam, on the same datasets:
model = inro.FUTON(
2, 3,
basis=("cosine", {"num_components": [H // 2, W // 2], "grid_size": (H, W)}),
combiner=("cp", {"rank": 280}),
head=("linear", {"bias": False}),
)
solver = inro.LeastSquares(model, momentum=0)
result = inro.train(model, dataset, dataset, optimizer=solver, num_steps=500, metrics={"psnr": inro.psnr})
On a grid with \(M_c\) coordinates along axis \(c\), the model is a CP tensor with one factor per axis, the response \(\boldsymbol{T}_c \boldsymbol{U}^{(c)}\) of the basis's table \(\boldsymbol{T}_c \in \mathbb{R}^{M_c \times K_c}\), and one for the channels, the head \(\boldsymbol{V}\). With the other blocks held, the normal equations of the mean squared error for \(\boldsymbol{U}^{(c)}\) are small, the size of the block itself:
where \(\boldsymbol{P}_c\) is the basis's Gram matrix and \(\boldsymbol{B}_c\) the targets' correlation with the rest of the model. The head's are \(\boldsymbol{V} \boldsymbol{G} = \boldsymbol{C}\), with \(\boldsymbol{G}\) the elementwise product of every axis's Gram matrix and \(\boldsymbol{C}\) the targets' correlation with the whole CP product.
Alternating least squares: every block takes its problem's solution, one side at a time, each with a ridge against collinear functions or components.
- From the left, by the inverse of \(\boldsymbol{P}_c\), which depends on the basis and the grid alone and is made once on a declared grid; from the right, by the Cholesky factorization of \(\boldsymbol{Q}_c\).
- Any basis will do, signed or not, and the parameters start from normal draws.
- With an activation at the output the targets are Gauss-Newton ones, refitted every sweep, so the sweeps descend the activated model's error rather than minimize a fixed one.
Lee and Seung's updates, for a nonnegative model. With everything nonnegative, the two parts of a block's gradient are too, and the update multiplies the block elementwise by their ratio:
- That is the minimizer of the auxiliary function Lee and Seung build for a quadratic with a nonnegative Hessian: the block's problem is never worse after the update, the block stays nonnegative, and one at a fixed point stays there. An entry is set to zero where a minibatch's estimate of \(\boldsymbol{B}_c\) is negative.
- A block's problem is fixed for the sweep and an update costs two small products, so each block takes
innerof them in a row, as Gillis and Glineur propose. - The model needs a nonnegative basis, the triangle with
normalize=Falseso that its functions sum to one, nonnegative targets and nonnegative parameters. A signal in \([-1, 1]\) is fitted through the affine output activationlambda z: 2 * z - 1.
A few facts hold for both:
- Batches. A batch is taken as points of the grid the basis declares, or else of the grid of its distinct coordinates. The Gram matrices are the grid's, and a right-hand side is what the model explains on the grid plus what it misses at the batch's points, \(\boldsymbol{B}_c = \boldsymbol{P}_c \boldsymbol{U}^{(c)} \boldsymbol{Q}_c + \boldsymbol{T}_c^\top \boldsymbol{E}_c / N\) for the residual's correlation \(\boldsymbol{E}_c\) over the \(N\) points. That is exact on a whole grid and, on a random share of it, the whole grid's in expectation, with the noise of the residual rather than of the signal. A sweep thus costs a few products of matrices over the grid whatever share of it the batch holds.
- Monotony. On a whole grid at
lr=1, no update ofMultiplicativeUpdate, or ofLeastSquareswithout its ridge, raises the batch's error. - Scales. Every axis's response is held at unit mean square, the head's columns taking the scales, which fixes the freedom of a CP model to trade scale between its blocks.
- Momentum.
momentumaverages each block's statistics over the batches, as online matrix factorization does (Mairal et al., 2010), with weights that sum to one from the first batch on. The newest batch's weight is the solver'slr: it starts at1 - momentum, andinro.trainanneals it to a hundredth. Without momentum it stays one, and every sweep solves the batch's own problem.
The notebook notebooks/futon_solvers.ipynb compares these solvers with Adam, and the error bound page uses LeastSquares on the whole grid.
futon_solvers
¶
Block coordinate descent for FUTON: solvers in place of a gradient optimizer.
A FUTON with a CP combiner and a linear head, both without bias, is multilinear, so
fitting one block with the others held is a least-squares problem. BCD sweeps
the blocks once per batch and leaves each block's update to a subclass:
LeastSquares solves the block's problem, for any basis, and
MultiplicativeUpdate takes Lee and Seung's step, which keeps a nonnegative
model nonnegative. inro.train takes both in place of Adam, on the same datasets.
BCD
¶
BCD(model: FUTON, momentum: float = 0.9, initialize: bool = True, **defaults: Any)
Block coordinate descent over a FUTON's factor matrices and head.
On a grid the model is a CP tensor, so with the other blocks held each block has
small normal equations, which a subclass's solve handles. step
updates the factors in order, then the head, and holds every axis's response at
unit mean square, the head's columns taking the scales.
A batch is taken as points of the grid the basis declares, or else of the grid of
its distinct coordinates. Only the residual comes from its points, so a sweep costs
about the same on any share of the grid, and a whole grid with momentum=0 is the
fast setting. On a whole grid at lr=1, no update of
MultiplicativeUpdate, or of LeastSquares without its ridge, raises
the batch's error.
Parameters:
-
model(FUTON) –FUTON with a CP combiner and a linear head, both without bias, whose basis evaluates one axis at a time through
axis_features, as the built-in ones do. Updated in place. -
momentum(float, default:0.9) –Weight of the past batches in a block's statistics, in
[0, 1), as in online matrix factorization;0takes each batch on its own. The solver'slr, the newest batch's weight, starts at1 - momentum. Above0,inro.trainanneals it to a hundredth, as it would a gradient step's; at0it stays one. -
initialize(bool, default:True) –Whether to draw the parameters anew with
reset_parameters;Falsekeeps the model's. -
**defaults(Any, default:{}) –A subclass's settings, kept in
self.defaults.
Raises:
-
ValueError–If
modelis not such a FUTON ormomentumis out of range.
References
Mairal et al., "Online learning for matrix factorization and sparse coding", JMLR 2010.
Source code in inro/models/futon_solvers.py
reset_parameters
¶
Draw the parameters as the solver wants them.
A subclass draws the entries. Each factor is then divided by the sum of its
basis's magnitudes in the middle of the domain, so that each axis's response
there is at most about one, and the head by 2 * R, so that its components
sum to about one half: the first sweep starts from an output of the signal's
order.
Source code in inro/models/futon_solvers.py
solve
¶
Return a block's new value, from its problem left @ value @ gram = right.
Parameters:
-
value(Tensor) –Current block,
(K_c, R)for a factor and(D, R)for the head. -
right(Tensor) –Right-hand side, of the block's shape.
-
gram(Tensor) –Elementwise product of the other blocks' Gram matrices,
(R, R). -
left(Tensor | None, default:None) –Gram matrix of the basis, in the form the subclass prepares it;
Noneis the identity, for the head.
Source code in inro/models/futon_solvers.py
compile
¶
Compile the sweep with torch.compile.
Parameters:
-
batch(Mapping[str, Tensor] | None, default:None) –Batch to compile on now, at its shapes, leaving the model and the solver as they were, so that no timed step pays for it;
Nonecompiles at the first step. -
**options(Any, default:{}) –Keyword arguments of
torch.compile.
Source code in inro/models/futon_solvers.py
step
¶
Run one sweep over the factor matrices and the head on a batch.
Parameters:
-
batch(Mapping[str, Tensor]) –input, coordinates(..., C)in[-1, 1], andtarget, their values(..., D), as a dataset yields them. The points must lie on a grid, as a coordinate dataset's do.
Returns:
-
Tensor–The batch's mean squared error before the sweep.
Source code in inro/models/futon_solvers.py
LeastSquares
¶
LeastSquares(model: FUTON, ridge: float = 0.001, momentum: float = 0.9, initialize: bool = True)
Alternating least squares: every block takes its problem's solution.
A block's problem is solved one side at a time, each with a ridge against collinear functions or components, for any basis, signed or not. With an output activation the targets are Gauss-Newton ones, refitted every sweep, so the sweeps descend the activated model's error rather than minimize a fixed one. The parameters start from normal draws.
Parameters:
-
model(FUTON) –See
BCD. -
ridge(float, default:0.001) –Added to the diagonal of each Gram matrix, relative to its mean diagonal.
-
momentum(float, default:0.9) –See
BCD. -
initialize(bool, default:True) –See
BCD.
Examples:
>>> model = FUTON(
... in_features=2,
... out_features=3,
... basis=("sinc", {"num_components": 16}),
... combiner=("cp", {"rank": 8}),
... head=("linear", {"bias": False}),
... )
>>> solver = LeastSquares(model, momentum=0)
>>> axis = torch.linspace(-1, 1, 32)
>>> x = torch.cartesian_prod(axis, axis)
>>> loss = solver.step({"input": x, "target": torch.rand(len(x), 3)})
>>> loss.shape
torch.Size([])
Source code in inro/models/futon_solvers.py
solve
¶
Return the block's ridged solution; see BCD.solve.
Source code in inro/models/futon_solvers.py
MultiplicativeUpdate
¶
MultiplicativeUpdate(model: FUTON, inner: int = 16, floor: float = 1e-12, momentum: float = 0.9, initialize: bool = True)
Lee and Seung's multiplicative updates, for a nonnegative model.
The update multiplies a block elementwise by the ratio of its gradient's two parts,
value * right / (left @ value @ gram). It never worsens the block's problem and
keeps the block nonnegative; each block takes inner of them in a row, as Gillis
and Glineur propose.
The first step checks that the basis, the targets and the parameters are
nonnegative. Use the triangle basis with normalize=False, whose functions sum to
one, and fit a signal in [-1, 1] through the affine output activation
lambda z: 2 * z - 1, under which its targets are in [0, 1]. The parameters
start uniformly in [0.5, 1.5], before scaling.
Parameters:
-
model(FUTON) –See
BCD. -
inner(int, default:16) –Number of updates of each block per sweep.
-
floor(float, default:1e-12) –Lower bound on a denominator, against division by zero where a basis function sees no sample.
-
momentum(float, default:0.9) –See
BCD. -
initialize(bool, default:True) –See
BCD.
References
Lee and Seung, "Algorithms for non-negative matrix factorization", NeurIPS 2001. Gillis and Glineur, "Accelerated multiplicative updates and hierarchical ALS algorithms for nonnegative matrix factorization", Neural Computation 2012.
Source code in inro/models/futon_solvers.py
solve
¶
Return the block after inner updates; see BCD.solve.
Source code in inro/models/futon_solvers.py
Error bounds¶
The error bound page states and proves the theorem these functions compute.
futon_bounds
¶
Bounds on how well a FUTON with a linear head can fit a sampled signal.
On a signal's sampling grid, the error of a FUTON with a CP combiner and a linear
head, both without bias, splits exactly in two: the error of the best approximation
in the basis's span, which no rank removes, and the CP misfit of the signal's
coefficients in an orthonormal basis of that span. futon_bound computes the
first exactly, brackets the best second, and returns the factors of a FUTON that attains
the upper bound. The documentation's error bound page states and proves the theorem.
FUTONBound
dataclass
¶
FUTONBound(ranks: list[int], truncation: float, lower: Tensor, upper: Tensor, factors: list[tuple[list[Tensor], Tensor]])
Bounds on the best mean squared error of a FUTON with a linear head.
Errors are float64 means over the grid's points and the signal's channels, as
inro.train reports them.
Attributes:
-
ranks(list[int]) –CP ranks, ascending.
-
truncation(float) –Error of the best approximation in the basis's span, which no rank goes below; exact.
-
lower(Tensor) –Per rank, a lower bound on the error of every FUTON of that rank; exact for a matrix of coefficients, that is, one axis, or two and one channel.
-
upper(Tensor) –Per rank, the error of the FUTON in
factors, and so an upper bound on the best one's. -
factors(list[tuple[list[Tensor], Tensor]]) –Per rank, the factors \(\boldsymbol{U}^{(c)}\),
(K_c, R), and the head \(\boldsymbol{V}\),(D, R), that attainupper.
initialize
¶
initialize(model: FUTON) -> None
Give model the factors and the head that attain upper at its rank.
The model's error on the bound's grid is then the bound, up to its precision.
Parameters:
-
model(FUTON) –FUTON with the bound's basis, a CP combiner and a linear head without bias.
Raises:
-
ValueError–If
modelhas another combiner or head, or a rank without a bound.
Source code in inro/models/futon_bounds.py
futon_bound
¶
futon_bound(signal: Tensor, features: Sequence[Tensor], ranks: int | Sequence[int], sweeps: int = 500, tol: float = 3e-05) -> FUTONBound
Bound the best grid error of a FUTON with a linear head on a signal.
Every FUTON with a CP combiner and a linear head, both without bias, with these
per-axis features and rank R has a mean squared error on the grid of at least
the bound's lower at R, and the one given by its factors has exactly its
upper; no FUTON goes below truncation. The bounds depend on the basis only
through each axis's span.
Parameters:
-
signal(Tensor) –Samples
(N_1, ..., N_C, D)on a grid. -
features(Sequence[Tensor]) –Per axis
c, the basis(N_c, K_c)at that axis's grid coordinates, orthonormal or not. For a FUTON's basis on a coordinate dataset's grid, it isbasis.axis_features(torch.linspace(-1, 1, N_c), c). -
ranks(int | Sequence[int]) –CP ranks
Rto bound. -
sweeps(int, default:500) –Maximum number of alternating least-squares sweeps that refine each upper bound, in float32, from its SVD form, the
Rheaviest terms of the nested SVDs;0keeps the SVD form. -
tol(float, default:3e-05) –Fraction of the bound below which a sweep's decrease stops the sweeps.
Returns:
-
FUTONBound–The bounds and the factors that attain the upper ones, on the CPU.
Raises:
-
ValueError–If
featuresis not one matrix ofN_crows per axis.
Examples:
>>> from inro import SincBasis
>>> signal = torch.rand(32, 32, 3) * 2 - 1
>>> basis = SincBasis(2, num_components=16)
>>> grid = torch.linspace(-1, 1, 32)
>>> features = [basis.axis_features(grid, c) for c in range(2)]
>>> bound = futon_bound(signal, features, ranks=[4, 8], sweeps=10)
>>> psnr = 10 * torch.log10(4 / bound.upper) # guaranteed, in dB
>>> psnr.shape
torch.Size([2])
Source code in inro/models/futon_bounds.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | |