Skip to main content

M2N

Struct M2N 

Source
pub struct M2N {
    pub moments: Vec<f64>,
    pub epsilon: f64,
    pub factor: f64,
    pub n_runs: usize,
    pub variant: usize,
    pub max_iter: usize,
    pub num_workers: isize,
    pub new_moments: Vec<f64>,
    pub parameters: Vec<f64>,
    pub error: f64,
}
Expand description

An EF3M fitter for a mixture of two Gaussians: the target moments, the search settings, and the best fit found so far.

Build it with M2N::new or M2N::with_defaults and run M2N::mp_fit or M2N::single_fit_loop. Parameter vectors are [mu_1, mu_2, sigma_1, sigma_2, p_1].

Fields§

§moments: Vec<f64>

Target raw moments E[x^k], k = 1..=5. The fit needs all five.

§epsilon: f64

Convergence tolerance on p_1; also the spacing of the mu_2 start grid, which has about 1 / epsilon points. Must be finite and > 0 (M2N::single_fit_loop checks).

§factor: f64

Width of the mu_2 start grid in standard deviations: starts run from m_1 + epsilon * factor * sigma to about m_1 + factor * sigma.

§n_runs: usize

Number of independent searches M2N::mp_fit runs.

§variant: usize

1 to fit four moments (M2N::iter_4) or 2 to fit five (M2N::iter_5).

§max_iter: usize

Maximum iterations of one attempt in M2N::fit; 0 runs none.

§num_workers: isize
👎Deprecated:

ignored: M2N::mp_fit runs serially

Ignored: M2N::mp_fit runs serially. Kept only so existing code still compiles.

§new_moments: Vec<f64>

Scratch: the moments implied by the last iterate of M2N::fit (or by M2N::get_moments with return_result = false).

§parameters: Vec<f64>

Best parameters found so far, [mu_1, mu_2, sigma_1, sigma_2, p_1]; all zeros until an admissible iterate improves on the initial error.

§error: f64

Squared moment error of parameters; starts at the sum of squared target moments.

Implementations§

Source§

impl M2N

Source

pub fn new( moments: Vec<f64>, epsilon: f64, factor: f64, n_runs: usize, variant: usize, max_iter: usize, num_workers: isize, ) -> Self

Creates a fitter; see the field docs for each argument. Nothing is validated here: M2N::single_fit_loop and M2N::mp_fit check moments and variant. num_workers is ignored (M2N::mp_fit runs serially).

parameters starts at zeros and error at the sum of squared moments.

Source

pub fn with_defaults(moments: Vec<f64>) -> Self

Creates a fitter with the defaults epsilon = 1e-5, factor = 5, n_runs = 1, variant = 2 (five moments, the more accurate variant) and max_iter = 100_000.

Source

pub fn get_moments( &mut self, parameters: &[f64; 5], return_result: bool, ) -> Option<Vec<f64>>

The first five raw moments E[x^k], k = 1..=5, of the mixture with the given parameters [mu_1, mu_2, sigma_1, sigma_2, p_1].

With return_result = true the moments are returned; with false they are stored in M2N::new_moments and None is returned. Taking exactly five parameters by type means a short slice cannot reach the indexing (convert a slice with slice.try_into()); the values themselves are not validated.

§Examples

See the module documentation.

Source

pub fn iter_4(&self, mu_2: f64, p_1: f64) -> Result<Vec<f64>, InputError>

One step of the four-moment variant (variant 1): from a guess of mu_2 and p_1, solves mu_1 from m_1, sigma_2 from m_3, sigma_1 from m_2, and a new p_1 from m_4.

Returns [mu_1, mu_2, sigma_1, sigma_2, p_1_new] (with mu_2 unchanged), or an empty vector if the step is inadmissible: a zero denominator, a negative variance, or a new p_1 outside [0, 1] (NaN included).

§Errors

InputError::TooShort if M2N::moments has fewer than four entries.

§Examples

See the module documentation.

Source

pub fn iter_5(&self, mu_2: f64, p_1: f64) -> Result<Vec<f64>, InputError>

One step of the five-moment variant. Like the paper, it takes the positive root for mu_2, so on raw moments it cannot return mu_2 < 0; single_fit_loop therefore runs this variant on centred moments (see centred_fit_loop).

From a guess of mu_2 and p_1, solves mu_1 from m_1, sigma_2 from m_3, sigma_1 from m_2, a new mu_2 from m_4 (positive root) and a new p_1 from m_5. Returns [mu_1, mu_2_new, sigma_1, sigma_2, p_1_new], or an empty vector if the step is inadmissible: a zero denominator, a negative variance or mu_2^2, 1 - p_1 < 1e-4, or a new p_1 outside [0, 1] (NaN included). Called directly on raw moments it still takes the positive root (#115).

§Errors

InputError::TooShort if M2N::moments has fewer than five entries.

§Examples

See the module documentation.

Source

pub fn fit(&mut self, mu_2: f64) -> Result<(), InputError>

One EF3M attempt from the starting mu_2 and a random starting p_1 in [0, 1).

Iterates M2N::iter_4 or M2N::iter_5 (by M2N::variant) until p_1 moves by less than M2N::epsilon, a step is inadmissible, or M2N::max_iter iterations have run. Whenever an iterate’s squared moment error (over the first five target moments) beats M2N::error, it replaces M2N::parameters and M2N::error, so they always hold the best iterate seen across calls, not the last one. The attempt runs on M2N::moments as given (for variant 2, on raw moments it cannot return mu_2 < 0); M2N::single_fit_loop is the entry point that centres them.

§Errors

Inadmissible steps and hitting max_iter end the attempt with Ok(()).

Source

pub fn single_fit_loop( &mut self, epsilon_override: Option<f64>, ) -> Result<Vec<FitResultRow>, InputError>

One EF3M search: runs M2N::fit from each start mu_2 = m_1 + i * epsilon * factor * sigma, i = 1, 2, ... up to about 1 / epsilon, where sigma is the standard deviation implied by the moments, and returns the best fit found.

epsilon_override, when Some, replaces M2N::epsilon. The search state (M2N::parameters, M2N::error) is reset first, and holds the best fit afterwards.

Variant 2 with a non-zero mean is fitted about the mean: the search runs on the moments of X - E[X] and the mean is added back to mu_1 and mu_2, so a negative mu_2 can be recovered (#115). The row’s error is always measured against the raw moments in M2N::moments.

Returns at most one row: none if no start produced an admissible iterate that improves on the initial error (the sum of squared moments). The result is random.

§Errors

Non-finite or inconsistent moments (for example a negative implied variance) are not errors: every attempt fails and the result is empty. The grid has about 1 / epsilon points, so the run time grows as epsilon shrinks.

use openquant::ef3m::M2N;

// Exact moments of 0.7 N(-1, 1) + 0.3 N(2, 0.5^2), fitted with five moments.
let truth = [-1.0, 2.0, 1.0, 0.5, 0.7];
let moments = M2N::with_defaults(vec![]).get_moments(&truth, true).unwrap();
let mut m2n = M2N::new(moments.clone(), 1e-4, 5.0, 1, 2, 100_000, 1);
let rows = m2n.single_fit_loop(None).unwrap();
assert_eq!(rows.len(), 1);
let fit = [rows[0].mu_1, rows[0].mu_2, rows[0].sigma_1, rows[0].sigma_2, rows[0].p_1];
// The starting p_1 is random, but at this epsilon the fit recovers the mixture.
assert!(fit.iter().zip(&truth).all(|(a, b)| (a - b).abs() < 0.05), "{fit:?}");
// The row's error is the squared error of its own parameters on the raw moments.
let implied = m2n.get_moments(&fit, true).unwrap();
let error: f64 = moments.iter().zip(&implied).map(|(a, b)| (a - b).powi(2)).sum();
assert!((error - rows[0].error).abs() < 1e-9);

// Fewer than five moments are rejected.
assert!(M2N::with_defaults(vec![0.0, 1.0]).single_fit_loop(None).is_err());
Source

pub fn mp_fit(&self) -> Result<Vec<FitResultRow>, InputError>

Runs M2N::single_fit_loop M2N::n_runs times, each on a fresh copy of self, and concatenates the rows (at most one per run). self is not modified.

The runs execute serially (the deprecated num_workers field is ignored). Summarise the rows with most_likely_parameters.

§Errors

Any error of M2N::single_fit_loop: InputError::TooShort for fewer than five moments, InputError::OutOfRange for a variant other than 1 or 2 or an epsilon that is not finite and > 0.

use openquant::ef3m::{most_likely_parameters, M2N};

let truth = [-1.0, 2.0, 1.0, 0.5, 0.7];
let moments = M2N::with_defaults(vec![]).get_moments(&truth, true).unwrap();
let rows = M2N::new(moments, 1e-4, 5.0, 5, 2, 100_000, 1).mp_fit().unwrap();
assert_eq!(rows.len(), 5);
let fit = most_likely_parameters(&rows, None, 1_000);
assert!((fit["mu_2"] - 2.0).abs() < 0.05 && (fit["p_1"] - 0.7).abs() < 0.05);

let bad_variant = M2N::new(vec![0.0; 5], 1e-4, 5.0, 1, 3, 100, 1);
assert!(bad_variant.mp_fit().is_err());

Trait Implementations§

Source§

impl Clone for M2N

Source§

fn clone(&self) -> M2N

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for M2N

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for M2N

§

impl RefUnwindSafe for M2N

§

impl Send for M2N

§

impl Sync for M2N

§

impl Unpin for M2N

§

impl UnsafeUnpin for M2N

§

impl UnwindSafe for M2N

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Key for T
where T: Clone,

§

fn align() -> usize

The alignment necessary for the key. Must return a power of two.
§

fn size(&self) -> usize

The size of the key in bytes.
§

unsafe fn init(&self, ptr: *mut u8)

Initialize the key in the given memory location. Read more
§

unsafe fn get<'a>(ptr: *const u8) -> &'a T

Get a reference to the key from the given memory location. Read more
§

unsafe fn drop_in_place(ptr: *mut u8)

Drop the key in place. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.