Estimator#

class torchsim.Estimator(acquisition=None)[source]#

Bases: Module

What every mapping method is, and the machinery all of them share.

DictionaryMatcher, LookupTable, NonlinearLeastSquares and PERK are all one of these. What they inherit is the whole problem statement – fit() naming the unknowns and drawing the training set from acquisition, map() returning one named map per unknown – so a method differs from the next only in what it does with plain tensors.

Writing another one is those two tensor steps. Subclass this, take the method’s own settings in the constructor, and implement _fit_arrays() and _estimate_arrays(), which see signals and parameters as matrices and know nothing about names, subspaces or simulators. Everything above them then works unchanged.

Parameters:

acquisition (Simulator, optional) – The sequence being inverted, with any tissue property that is neither unknown nor measured already fixed on it. Left out where the estimator is given signals directly.

acquisition#

The sequence being inverted.

Type:

Simulator or None

noise_std#

The noise fit() was told the measurement has.

Type:

float or torch.Tensor

rank#

The rank fit() was asked to compress to, if any.

Type:

int or None

seed#

The seed the training draw used, if one was given.

Type:

int or None

Methods

fit

Fit the estimator over a sampling of the tissue it will meet.

from_coefficients

Map coefficients that are already in this estimator's basis.

map

Estimate the tissue a measurement came from.

training_set

Simulate a training set: signals, unknowns, and knowns.

uncertainty_of

The standard deviation the measurement noise leaves on each map.

property unknown#

The properties being estimated, in the order the maps come back.

property known#

The properties measured separately, in the order they are given.

property subspace#

The temporal basis in use, or None if the estimator works in full.

property trained#

Whether the estimator has what it needs to map.

A dictionary handed straight to the constructor counts, which is why this consults the subclass rather than only recording that fit() was called.

fit(unknown=None, *, known=None, noise_std=0.0, rank=None, subspace=None, seed=None, samples=None, chunk=_CHUNK, signals=None, parameters=None, **ranges)[source]#

Fit the estimator over a sampling of the tissue it will meet.

Parameters:
  • unknown (mapping, optional) – The properties being estimated, as {name: (low, high)} to draw uniformly over, or an array of values to use as given. The order is the order of the maps that come back. Naming them as keywords instead is the same thing and is usually shorter; a mapping is the way to name one that collides with a keyword here.

  • known (mapping or torch.Tensor, optional) – Properties measured separately, as {name: range} or {name: values}. They are drawn over here and reach the simulator, so the training signals carry their effect, and the measured maps are given again to map(). A tensor alongside signals, where the columns are given outright.

  • noise_std (float or torch.Tensor, optional) – Standard deviation of the noise added to the training signals, in the units the signal is in – a fully relaxed voxel is 1. This is what teaches a method how far to trust a measurement, so it should be the noise the scan actually has.

  • rank (int, optional) – Fit a temporal Subspace of this rank to the training signals and work in it. Every contrast dropped is arithmetic neither training nor mapping has to do; subspace reports what the compression kept.

  • subspace (Subspace, optional) – Work in a basis fitted elsewhere rather than fitting one here – the one another estimator carries, or one simulate_subspace() produced. This is what lets a reconstruction and the estimator that reads its coefficients agree on the basis by construction. Not with rank, which asks for a basis to be fitted.

  • seed (int, optional) – Seed for the training draw.

  • samples (int, optional) – How many tissues to draw. Stating a property as an array of values fixes this, so it may be left out; where everything is a range to draw from, it defaults to ten thousand.

  • chunk (int, optional) – How many to simulate at a time.

  • signals (torch.Tensor, optional) – (samples, contrasts) given outright, instead of simulated. The acquisition is then not consulted and map() returns a tensor rather than named maps, unless names were given as well.

  • parameters (torch.Tensor, optional) – (samples, unknowns), alongside signals.

  • ranges – The unknown properties, named as keywords.

Return type:

This estimator, fitted.

Raises:
  • RuntimeError – If nothing is given to fit from: no signals, and no acquisition to simulate them.

  • ValueError – If a name is both unknown and known, if a range is not a pair, if rank is not positive, or if both rank and subspace are given.

training_set(samples, *, chunk=_CHUNK)[source]#

Simulate a training set: signals, unknowns, and knowns.

Memory is samples by contrasts, because the signals are held once rather than resimulated for each pass a method makes over them. The properties are the ones fit() was given, so this is what a caller wanting the training signals themselves calls after fitting.

Parameters:
  • samples (int) – How many tissues to draw.

  • chunk (int, optional) – How many to simulate at a time.

Returns:

(signals, parameters, known), the last None when nothing is measured separately.

Return type:

tuple

Raises:
  • RuntimeError – If the estimator has no acquisition, or was never told what is unknown.

  • ValueError – If samples or chunk is not positive, or if an array given for a property has a different length.

map(volume, known=None, *, uncertainty=False)[source]#

Estimate the tissue a measurement came from.

Parameters:
  • volume (array-like) – (..., contrasts). Every leading axis is the voxel axis and comes back on the maps unchanged.

  • known (mapping or torch.Tensor, optional) – The measured maps, under the names the estimator was given. Each is broadcast to the voxel shape.

  • uncertainty (bool, optional) – Also return the standard deviation the noise leaves on each map. See uncertainty_of() for what that number is and what it is not. Not every method can state one.

Returns:

{name: map} where the estimator was told what it is solving for, each shaped like volume without its contrast axis. Where it was fitted from bare arrays, the parameter columns as a tensor. With uncertainty=True, a pair of those – the maps, and the standard deviations beside them.

Return type:

dict or torch.Tensor

Raises:
  • RuntimeError – If the estimator has not been fitted.

  • NotImplementedError – If uncertainty is asked of a method that does not state one.

  • ValueError – If a measured map is missing, or has the wrong voxel count.

forward(volume, known=None, *, uncertainty=False)[source]#

Alias of map(), so a fitted estimator is callable.

uncertainty_of(volume, known=None)[source]#

The standard deviation the measurement noise leaves on each map.

The noise fit() was told about, propagated through the estimate this method makes – so it says how much the answer would move if the scan were repeated, and it is a property of the method as much as of the sequence.

It is not the error. An estimator that is biased is biased the same way in every realization, so repeating the measurement never reveals that part and this number does not contain it. Read against crlb(), which is the lowest standard deviation an unbiased estimate could have, it says how much of the distance to the truth the acquisition is responsible for.

Parameters:
  • volume (array-like) – (..., contrasts), as map() takes.

  • known (mapping or torch.Tensor, optional) – The measured maps, as map() takes.

Returns:

One standard deviation per unknown, shaped like the maps.

Return type:

dict or torch.Tensor

Raises:

NotImplementedError – If this method does not state one.

from_coefficients(coefficients, known=None)[source]#

Map coefficients that are already in this estimator’s basis.

A subspace reconstruction solves for the coefficients directly and never forms the contrast images, so what it returns is already projected. map() would project it a second time; this does not. The basis it is in is subspace, which is also what the reconstruction was given.

Parameters:
  • coefficients (array-like) – (..., rank). Every leading axis is a voxel axis.

  • known (mapping, optional) – The measured maps, as for map().

Returns:

{name: map}, shaped like coefficients without its last axis.

Return type:

dict

Raises:
  • RuntimeError – If the estimator has not been fitted, or was fitted without a rank, in which case there is no basis for these to be in.

  • ValueError – If the last axis is not the rank of that basis.